Merge branch 'machine-abstraction'
This branch brings in the "machine abstraction" code. This is a major milestone in the development of Vagrant as it abstracts all of the VirtualBox-specific code out into a plugin. There is zero VirtualBox specific code in the core ("lib/") directory at this point. Read on for important points. == Gotchas White it is technically possible now to write plugins for other providers, there is still major work to be done to make this feasible. The plugin interface itself is pretty much done, but there are some issues: * ":virtualbox" is the hardcoded provider to be used at the moment. * There is no way to configure a provider. For example, `config.vm.customize` would never work for anything other than VirtualBox, so there needs to be a way to have provider-specific configuration. This will come soon. * Shared folders and networking need to be rearchitected to be friendly for multiple providers, since it is unrealistic that a provider such as EC2 could provide the same level of networking, for example. * There is no way easy way (like `vagrant package --base`) to create boxes for providers other than VirtualBox. This will be addressed in a whole new feature of Vagrant probably in a future release after provider stuff has shipped. == Writing a Provider To write a provider, you create a Vagrant plugin that defines a "provider". See the "plugins/providers/virtualbox/plugin.rb" for more details. Providers themselves have an exremely simple API. The burden for writing providers mostly rests on the fact that you must define complex middleware sequences. Lots more work to come in the future, but this is a BIG MILESTONE!
This commit is contained in:
commit
391dc39267
|
@ -66,7 +66,6 @@ module Vagrant
|
|||
autoload :BoxCollection, 'vagrant/box_collection'
|
||||
autoload :CLI, 'vagrant/cli'
|
||||
autoload :Command, 'vagrant/command'
|
||||
autoload :Communication, 'vagrant/communication'
|
||||
autoload :Config, 'vagrant/config'
|
||||
autoload :DataStore, 'vagrant/data_store'
|
||||
autoload :Downloaders, 'vagrant/downloaders'
|
||||
|
@ -76,27 +75,23 @@ module Vagrant
|
|||
autoload :Errors, 'vagrant/errors'
|
||||
autoload :Guest, 'vagrant/guest'
|
||||
autoload :Hosts, 'vagrant/hosts'
|
||||
autoload :Machine, 'vagrant/machine'
|
||||
autoload :Plugin, 'vagrant/plugin'
|
||||
autoload :SSH, 'vagrant/ssh'
|
||||
autoload :TestHelpers, 'vagrant/test_helpers'
|
||||
autoload :UI, 'vagrant/ui'
|
||||
autoload :Util, 'vagrant/util'
|
||||
autoload :VM, 'vagrant/vm'
|
||||
|
||||
# These are the various plugin versions and their components in
|
||||
# a lazy loaded Hash-like structure.
|
||||
c = PLUGIN_COMPONENTS = Registry.new
|
||||
PLUGIN_COMPONENTS = Registry.new.tap do |c|
|
||||
c.register(:"1") { Plugin::V1::Plugin }
|
||||
c.register([:"1", :command]) { Plugin::V1::Command }
|
||||
c.register([:"1", :communicator]) { Plugin::V1::Communicator }
|
||||
c.register([:"1", :config]) { Plugin::V1::Config }
|
||||
c.register([:"1", :guest]) { Plugin::V1::Guest }
|
||||
c.register([:"1", :host]) { Plugin::V1::Host }
|
||||
c.register([:"1", :provider]) { Plugin::V1::Provider }
|
||||
c.register([:"1", :provisioner]) { Plugin::V1::Provisioner }
|
||||
|
||||
# Returns a `Vagrant::Registry` object that contains all the built-in
|
||||
# middleware stacks.
|
||||
def self.actions
|
||||
@actions ||= Vagrant::Action::Builtin.new
|
||||
end
|
||||
|
||||
# The source root is the path to the root directory of
|
||||
|
|
|
@ -2,60 +2,35 @@ require 'vagrant/action/builder'
|
|||
|
||||
module Vagrant
|
||||
module Action
|
||||
autoload :Builtin, 'vagrant/action/builtin'
|
||||
autoload :Environment, 'vagrant/action/environment'
|
||||
autoload :Runner, 'vagrant/action/runner'
|
||||
autoload :Warden, 'vagrant/action/warden'
|
||||
|
||||
module Box
|
||||
autoload :Add, 'vagrant/action/box/add'
|
||||
autoload :Download, 'vagrant/action/box/download'
|
||||
autoload :Verify, 'vagrant/action/box/verify'
|
||||
end
|
||||
|
||||
module Env
|
||||
autoload :Set, 'vagrant/action/env/set'
|
||||
# Builtin contains middleware classes that are shipped with Vagrant-core
|
||||
# and are thus available to all plugins as a "standard library" of sorts.
|
||||
module Builtin
|
||||
autoload :BoxAdd, "vagrant/action/builtin/box_add"
|
||||
autoload :Call, "vagrant/action/builtin/call"
|
||||
autoload :Confirm, "vagrant/action/builtin/confirm"
|
||||
autoload :EnvSet, "vagrant/action/builtin/env_set"
|
||||
autoload :SSHExec, "vagrant/action/builtin/ssh_exec"
|
||||
autoload :SSHRun, "vagrant/action/builtin/ssh_run"
|
||||
end
|
||||
|
||||
module General
|
||||
autoload :CheckVirtualbox, 'vagrant/action/general/check_virtualbox'
|
||||
autoload :Package, 'vagrant/action/general/package'
|
||||
autoload :Validate, 'vagrant/action/general/validate'
|
||||
end
|
||||
|
||||
module VM
|
||||
autoload :Boot, 'vagrant/action/vm/boot'
|
||||
autoload :CheckAccessible, 'vagrant/action/vm/check_accessible'
|
||||
autoload :CheckBox, 'vagrant/action/vm/check_box'
|
||||
autoload :CheckGuestAdditions, 'vagrant/action/vm/check_guest_additions'
|
||||
autoload :CheckPortCollisions, 'vagrant/action/vm/check_port_collisions'
|
||||
autoload :CleanMachineFolder, 'vagrant/action/vm/clean_machine_folder'
|
||||
autoload :ClearForwardedPorts, 'vagrant/action/vm/clear_forwarded_ports'
|
||||
autoload :ClearNetworkInterfaces, 'vagrant/action/vm/clear_network_interfaces'
|
||||
autoload :ClearSharedFolders, 'vagrant/action/vm/clear_shared_folders'
|
||||
autoload :Customize, 'vagrant/action/vm/customize'
|
||||
autoload :DefaultName, 'vagrant/action/vm/default_name'
|
||||
autoload :Destroy, 'vagrant/action/vm/destroy'
|
||||
autoload :DestroyUnusedNetworkInterfaces, 'vagrant/action/vm/destroy_unused_network_interfaces'
|
||||
autoload :DiscardState, 'vagrant/action/vm/discard_state'
|
||||
autoload :Export, 'vagrant/action/vm/export'
|
||||
autoload :ForwardPorts, 'vagrant/action/vm/forward_ports'
|
||||
autoload :Halt, 'vagrant/action/vm/halt'
|
||||
autoload :HostName, 'vagrant/action/vm/host_name'
|
||||
autoload :Import, 'vagrant/action/vm/import'
|
||||
autoload :MatchMACAddress, 'vagrant/action/vm/match_mac_address'
|
||||
autoload :Network, 'vagrant/action/vm/network'
|
||||
autoload :NFS, 'vagrant/action/vm/nfs'
|
||||
autoload :Package, 'vagrant/action/vm/package'
|
||||
autoload :PackageVagrantfile, 'vagrant/action/vm/package_vagrantfile'
|
||||
autoload :Provision, 'vagrant/action/vm/provision'
|
||||
autoload :ProvisionerCleanup, 'vagrant/action/vm/provisioner_cleanup'
|
||||
autoload :PruneNFSExports, 'vagrant/action/vm/prune_nfs_exports'
|
||||
autoload :Resume, 'vagrant/action/vm/resume'
|
||||
autoload :SaneDefaults, 'vagrant/action/vm/sane_defaults'
|
||||
autoload :ShareFolders, 'vagrant/action/vm/share_folders'
|
||||
autoload :SetupPackageFiles, 'vagrant/action/vm/setup_package_files'
|
||||
autoload :Suspend, 'vagrant/action/vm/suspend'
|
||||
# This is the action that will add a box from a URL. This middleware
|
||||
# sequence is built-in to Vagrant. Plugins can hook into this like any
|
||||
# other middleware sequence. This is particularly useful for provider
|
||||
# plugins, which can hook in to do things like verification of boxes
|
||||
# that are downloaded.
|
||||
def self.action_box_add
|
||||
Builder.new.tap do |b|
|
||||
b.use Builtin::BoxAdd
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Box
|
||||
# Adds a downloaded box file to the environment's box collection.
|
||||
# This handles unpacking the box. See {BoxCollection#add} for more
|
||||
# information.
|
||||
class Add
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => env[:box_name])
|
||||
|
||||
begin
|
||||
env[:box_collection].add(env[:box_download_temp_path], env[:box_name])
|
||||
rescue Vagrant::Errors::BoxUpgradeRequired
|
||||
# Upgrade the box
|
||||
env[:box_collection].upgrade(env[:box_name])
|
||||
|
||||
# Try adding it again
|
||||
retry
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,84 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Box
|
||||
class Download
|
||||
BASENAME = "box"
|
||||
|
||||
include Util
|
||||
|
||||
attr_reader :temp_path
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
@env["download.classes"] ||= []
|
||||
@env["download.classes"] += [Downloaders::HTTP, Downloaders::File]
|
||||
@downloader = nil
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env = env
|
||||
|
||||
download if instantiate_downloader
|
||||
@app.call(@env)
|
||||
|
||||
recover(env) # called in both cases to cleanup workspace
|
||||
end
|
||||
|
||||
def instantiate_downloader
|
||||
# Assign to a temporary variable since this is easier to type out,
|
||||
# since it is used so many times.
|
||||
classes = @env["download.classes"]
|
||||
|
||||
# Find the class to use.
|
||||
classes.each_index do |i|
|
||||
klass = classes[i]
|
||||
|
||||
# Use the class if it matches the given URI or if this
|
||||
# is the last class...
|
||||
if classes.length == (i + 1) || klass.match?(@env[:box_url])
|
||||
@env[:ui].info I18n.t("vagrant.actions.box.download.with", :class => klass.to_s)
|
||||
@downloader = klass.new(@env[:ui])
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# This line should never be reached, but we'll keep this here
|
||||
# just in case for now.
|
||||
raise Errors::BoxDownloadUnknownType if !@downloader
|
||||
|
||||
@downloader.prepare(@env[:box_url])
|
||||
true
|
||||
end
|
||||
|
||||
def download
|
||||
with_tempfile do |tempfile|
|
||||
download_to(tempfile)
|
||||
@temp_path = @env[:box_download_temp_path] = tempfile.path
|
||||
end
|
||||
end
|
||||
|
||||
def recover(env)
|
||||
if temp_path && File.exist?(temp_path)
|
||||
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
|
||||
File.unlink(temp_path)
|
||||
end
|
||||
end
|
||||
|
||||
def with_tempfile
|
||||
File.open(box_temp_path, Platform.tar_file_options) do |tempfile|
|
||||
yield tempfile
|
||||
end
|
||||
end
|
||||
|
||||
def box_temp_path
|
||||
@env[:tmp_path].join(BASENAME + Time.now.to_i.to_s)
|
||||
end
|
||||
|
||||
def download_to(f)
|
||||
@downloader.download!(@env[:box_url], f)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Box
|
||||
class Verify
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env[:ui].info I18n.t("vagrant.actions.box.verify.verifying")
|
||||
|
||||
box = env[:box_collection].find(env[:box_name], :virtualbox)
|
||||
driver = Driver::VirtualBox.new(nil)
|
||||
if !driver.verify_image(box.directory.join("box.ovf").to_s)
|
||||
raise Errors::BoxVerificationFailed
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,18 +9,21 @@ module Vagrant
|
|||
#
|
||||
# Building an action sequence is very easy:
|
||||
#
|
||||
# app = Vagrant::Action::Builder.new do
|
||||
# use MiddlewareA
|
||||
# use MiddlewareB
|
||||
# app = Vagrant::Action::Builder.new.tap do |b|
|
||||
# b.use MiddlewareA
|
||||
# b.use MiddlewareB
|
||||
# end
|
||||
#
|
||||
# Vagrant::Action.run(app)
|
||||
#
|
||||
class Builder
|
||||
# Initializes the builder. An optional block can be passed which
|
||||
# will be evaluated in the context of the instance.
|
||||
def initialize(&block)
|
||||
instance_eval(&block) if block_given?
|
||||
# This is a shortcut for a middleware sequence with only one item
|
||||
# in it. For a description of the arguments and the documentation, please
|
||||
# see {#use} instead.
|
||||
#
|
||||
# @return [Builder]
|
||||
def self.build(middleware, *args, &block)
|
||||
new.use(middleware, *args, &block)
|
||||
end
|
||||
|
||||
# Returns a mergeable version of the builder. If `use` is called with
|
||||
|
@ -38,11 +41,6 @@ module Vagrant
|
|||
#
|
||||
# @param [Class] middleware The middleware class
|
||||
def use(middleware, *args, &block)
|
||||
# Prepend with a environment setter if args are given
|
||||
if !args.empty? && args.first.is_a?(Hash) && middleware != Env::Set
|
||||
self.use(Env::Set, args.shift, &block)
|
||||
end
|
||||
|
||||
if middleware.kind_of?(Builder)
|
||||
# Merge in the other builder's stack into our own
|
||||
self.stack.concat(middleware.stack)
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
# A registry object containing the built-in middleware stacks.
|
||||
class Builtin < Registry
|
||||
def initialize
|
||||
# Properly initialize the registry object
|
||||
super
|
||||
|
||||
# Register all the built-in stacks
|
||||
register_builtin!
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def register_builtin!
|
||||
# We do this so that the blocks below have a variable to access the
|
||||
# outer registry.
|
||||
registry = self
|
||||
|
||||
# provision - Provisions a running VM
|
||||
register(:provision) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::Provision
|
||||
end
|
||||
end
|
||||
|
||||
# start - Starts a VM, assuming it already exists on the
|
||||
# environment.
|
||||
register(:start) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::CleanMachineFolder
|
||||
use VM::ClearForwardedPorts
|
||||
use VM::CheckPortCollisions, :port_collision_handler => :correct
|
||||
use VM::ForwardPorts
|
||||
use VM::Provision
|
||||
use VM::PruneNFSExports
|
||||
use VM::NFS
|
||||
use VM::ClearSharedFolders
|
||||
use VM::ShareFolders
|
||||
use VM::ClearNetworkInterfaces
|
||||
use VM::Network
|
||||
use VM::HostName
|
||||
use VM::SaneDefaults
|
||||
use VM::Customize
|
||||
use VM::Boot
|
||||
end
|
||||
end
|
||||
|
||||
# halt - Halts the VM, attempting gracefully but then forcing
|
||||
# a restart if fails.
|
||||
register(:halt) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::DiscardState
|
||||
use VM::Halt
|
||||
end
|
||||
end
|
||||
|
||||
# suspend - Suspends the VM
|
||||
register(:suspend) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::Suspend
|
||||
end
|
||||
end
|
||||
|
||||
# resume - Resume a VM
|
||||
register(:resume) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::CheckPortCollisions
|
||||
use VM::Resume
|
||||
end
|
||||
end
|
||||
|
||||
# reload - Halts then restarts the VM
|
||||
register(:reload) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use registry.get(:halt)
|
||||
use registry.get(:start)
|
||||
end
|
||||
end
|
||||
|
||||
# up - Imports, prepares, then starts a fresh VM.
|
||||
register(:up) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use VM::CheckBox
|
||||
use VM::Import
|
||||
use VM::CheckGuestAdditions
|
||||
use VM::DefaultName
|
||||
use VM::MatchMACAddress
|
||||
use registry.get(:start)
|
||||
end
|
||||
end
|
||||
|
||||
# destroy - Halts, cleans up, and destroys an existing VM
|
||||
register(:destroy) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::CheckAccessible
|
||||
use registry.get(:halt), :force => true
|
||||
use VM::ProvisionerCleanup
|
||||
use VM::PruneNFSExports
|
||||
use VM::Destroy
|
||||
use VM::CleanMachineFolder
|
||||
use VM::DestroyUnusedNetworkInterfaces
|
||||
end
|
||||
end
|
||||
|
||||
# package - Export and package the VM
|
||||
register(:package) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use General::Validate
|
||||
use VM::SetupPackageFiles
|
||||
use VM::CheckAccessible
|
||||
use registry.get(:halt)
|
||||
use VM::ClearForwardedPorts
|
||||
use VM::ClearSharedFolders
|
||||
use VM::Export
|
||||
use VM::PackageVagrantfile
|
||||
use VM::Package
|
||||
end
|
||||
end
|
||||
|
||||
# box_add - Download and add a box.
|
||||
register(:box_add) do
|
||||
Builder.new do
|
||||
use General::CheckVirtualbox
|
||||
use Box::Download
|
||||
use Box::Add
|
||||
use Box::Verify
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,75 @@
|
|||
require "vagrant/util/platform"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This middleware will download a remote box and add it to the
|
||||
# given box collection.
|
||||
class BoxAdd
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Instantiate the downloader
|
||||
downloader = download_klass(env[:box_url]).new(env[:ui])
|
||||
env[:ui].info I18n.t("vagrant.actions.box.download.with",
|
||||
:class => downloader.class.to_s)
|
||||
|
||||
# Download the box to a temporary path. We store the temporary
|
||||
# path as an instance variable so that the `#recover` method can
|
||||
# access it.
|
||||
@temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s)
|
||||
File.open(@temp_path, Vagrant::Util::Platform.tar_file_options) do |f|
|
||||
downloader.download!(env[:box_url], f)
|
||||
end
|
||||
|
||||
# Add the box
|
||||
env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => env[:box_name])
|
||||
begin
|
||||
env[:box_collection].add(@temp_path, env[:box_name])
|
||||
rescue Vagrant::Errors::BoxUpgradeRequired
|
||||
# Upgrade the box
|
||||
env[:box_collection].upgrade(env[:box_name])
|
||||
|
||||
# Try adding it again
|
||||
retry
|
||||
end
|
||||
|
||||
# Call the 'recover' method in all cases to clean up the
|
||||
# downloaded temporary file.
|
||||
recover(env)
|
||||
|
||||
# Carry on!
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def download_klass(url)
|
||||
# This is hardcoded for now. In the future I'd like to make this
|
||||
# pluginnable as well.
|
||||
classes = [Downloaders::HTTP, Downloaders::File]
|
||||
|
||||
# Find the class to use.
|
||||
classes.each_index do |i|
|
||||
klass = classes[i]
|
||||
|
||||
# Use the class if it matches the given URI or if this
|
||||
# is the last class...
|
||||
return klass if classes.length == (i + 1) || klass.match?(url)
|
||||
end
|
||||
|
||||
# If no downloader knows how to download this file, then we
|
||||
# raise an exception.
|
||||
raise Errors::BoxDownloadUnknownType
|
||||
end
|
||||
|
||||
def recover(env)
|
||||
if @temp_path && File.exist?(@temp_path)
|
||||
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
|
||||
File.unlink(@temp_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This middleware class allows a sort of "conditional" run within
|
||||
# a single middlware sequence. It takes another middleware runnable,
|
||||
# runs it with the same environment, then yields the resulting env to a block,
|
||||
# allowing that block to determine the next course of action in the
|
||||
# middleware sequence.
|
||||
#
|
||||
# The first argument to this middleware sequence is anywhere middleware
|
||||
# runnable, whether it be a class, lambda, or something else that
|
||||
# responds to `call`. This middleware runnable is run with the same
|
||||
# environment as this class.
|
||||
#
|
||||
# After running, {Call} takes the environment and yields it to a block
|
||||
# given to initialize the class, along with an instance of {Builder}.
|
||||
# The result is used to build up a new sequence on the given builder.
|
||||
# This builder is then run.
|
||||
class Call
|
||||
# For documentation, read the description of the {Call} class.
|
||||
#
|
||||
# @param [Object] callable A valid middleware runnable object. This
|
||||
# can be a class, a lambda, or an object that responds to `call`.
|
||||
# @yield [result, builder] This block is expected to build on `builder`
|
||||
# which is the next middleware sequence that will be run.
|
||||
def initialize(app, env, callable, &block)
|
||||
raise ArgumentError, "A block must be given to Call" if !block
|
||||
|
||||
@app = app
|
||||
@callable = callable
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call(env)
|
||||
runner = Runner.new
|
||||
|
||||
# Run our callable with our environment
|
||||
new_env = runner.run(@callable, env)
|
||||
|
||||
# Build our new builder based on the result
|
||||
builder = Builder.new
|
||||
@block.call(new_env, builder)
|
||||
|
||||
# Run the result with our new environment
|
||||
final_env = runner.run(builder, new_env)
|
||||
|
||||
# Call the next step using our final environment
|
||||
@app.call(final_env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This class asks the user to confirm some sort of question with
|
||||
# a "Y/N" question. The only parameter is the text to ask the user.
|
||||
# The result is placed in `env[:result]` so that it can be used
|
||||
# with the {Call} class.
|
||||
class Confirm
|
||||
# For documentation, read the description of the {Confirm} class.
|
||||
#
|
||||
# @param [String] message The message to ask the user.
|
||||
def initialize(app, env, message)
|
||||
@app = app
|
||||
@message = message
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Ask the user the message and store the result
|
||||
choice = nil
|
||||
choice = env[:ui].ask(@message)
|
||||
env[:result] = choice && choice.upcase == "Y"
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This middleware class allows you to modify the environment hash
|
||||
# in the middle of a middleware sequence. The new environmental data
|
||||
# will take affect at this stage in the middleware and will persist
|
||||
# through.
|
||||
class EnvSet
|
||||
def initialize(app, env, new_env=nil)
|
||||
@app = app
|
||||
@new_env = new_env || {}
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Merge in the new data
|
||||
env.merge!(@new_env)
|
||||
|
||||
# Carry on
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
require "vagrant/util/ssh"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This class will exec into a full fledged SSH console into the
|
||||
# remote machine. This middleware assumes that the VM is running and
|
||||
# ready for SSH, and uses the {Machine#ssh_info} method to retrieve
|
||||
# SSH information necessary to connect.
|
||||
#
|
||||
# Note: If there are any middleware after `SSHExec`, they will **not**
|
||||
# run, since exec replaces the currently running process.
|
||||
class SSHExec
|
||||
# For quick access to the `SSH` class.
|
||||
include Vagrant::Util
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Grab the SSH info from the machine
|
||||
info = env[:machine].ssh_info
|
||||
|
||||
# If the result is nil, then the machine is telling us that it is
|
||||
# not yet ready for SSH, so we raise this exception.
|
||||
raise Errors::SSHNotReady if info.nil?
|
||||
|
||||
# Check the SSH key permissions
|
||||
SSH.check_key_permissions(info[:private_key_path])
|
||||
|
||||
# Exec!
|
||||
SSH.exec(info, env[:ssh_opts])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This class will run a single command on the remote machine and will
|
||||
# mirror the output to the UI. The resulting exit status of the command
|
||||
# will exist in the `:ssh_run_exit_status` key in the environment.
|
||||
class SSHRun
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant::action::builtin::ssh_run")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
command = env[:ssh_run_command]
|
||||
|
||||
@logger.debug("Executing command: #{command}")
|
||||
exit_status = 0
|
||||
exit_status = env[:machine].communicate.execute(command, :error_check => false) do |type, data|
|
||||
# Determine the proper channel to send the output onto depending
|
||||
# on the type of data we are receiving.
|
||||
channel = type == :stdout ? :out : :error
|
||||
|
||||
# Print the output as it comes in, but don't prefix it and don't
|
||||
# force a new line so that the output is properly preserved however
|
||||
# it may be formatted.
|
||||
env[:ui].info(data.to_s,
|
||||
:prefix => false,
|
||||
:new_line => false,
|
||||
:channel => channel)
|
||||
end
|
||||
|
||||
# Set the exit status on a known environmental variable
|
||||
env[:ssh_run_exit_status] = exit_status
|
||||
|
||||
# Call the next middleware
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module Env
|
||||
# A middleware which just sets up the environment with some
|
||||
# options which are passed to it.
|
||||
class Set
|
||||
def initialize(app, env, options=nil)
|
||||
@app = app
|
||||
@options = options || {}
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Merge the options that were given to us
|
||||
env.merge!(@options)
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module General
|
||||
# Checks that virtualbox is installed and ready to be used.
|
||||
class CheckVirtualbox
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Certain actions may not actually have a VM, and thus no
|
||||
# driver, so we have to be clever about obtaining an instance
|
||||
# of the driver.
|
||||
driver = nil
|
||||
driver = env[:vm].driver if env[:vm]
|
||||
driver = Driver::VirtualBox.new(nil) if !driver
|
||||
|
||||
# Verify that it is ready to go! This will raise an exception
|
||||
# if anything goes wrong.
|
||||
driver.verify!
|
||||
|
||||
# Carry on.
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,10 @@ module Vagrant
|
|||
end
|
||||
|
||||
def call(env)
|
||||
env[:vm].config.validate!(env[:vm].env) if !env.has_key?("validate") || env["validate"]
|
||||
if !env.has_key?(:validate) || env[:validate]
|
||||
env[:machine].config.validate!(env[:machine].env)
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,8 +10,7 @@ module Vagrant
|
|||
class Runner
|
||||
@@reported_interrupt = false
|
||||
|
||||
def initialize(registry, globals=nil, &block)
|
||||
@registry = registry
|
||||
def initialize(globals=nil, &block)
|
||||
@globals = globals || {}
|
||||
@lazy_globals = block
|
||||
@logger = Log4r::Logger.new("vagrant::action::runner")
|
||||
|
@ -19,8 +18,7 @@ module Vagrant
|
|||
|
||||
def run(callable_id, options=nil)
|
||||
callable = callable_id
|
||||
callable = Builder.new.use(callable_id) if callable_id.kind_of?(Class)
|
||||
callable = registry_sequence(callable_id) if callable_id.kind_of?(Symbol)
|
||||
callable = Builder.build(callable_id) if callable_id.kind_of?(Class)
|
||||
raise ArgumentError, "Argument to run must be a callable object or registered action." if !callable || !callable.respond_to?(:call)
|
||||
|
||||
# Create the initial environment with the options given
|
||||
|
@ -47,28 +45,10 @@ module Vagrant
|
|||
# We place a process lock around every action that is called
|
||||
@logger.info("Running action: #{callable_id}")
|
||||
Util::Busy.busy(int_callback) { callable.call(environment) }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def registry_sequence(id)
|
||||
# Attempt to get the sequence
|
||||
seq = @registry.get(id)
|
||||
return nil if !seq
|
||||
|
||||
# Go through all the registered plugins and get all the hooks
|
||||
# for this sequence.
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
hooks = plugin.action_hook(Vagrant::Plugin::V1::Plugin::ALL_ACTIONS)
|
||||
hooks += plugin.action_hook(id)
|
||||
|
||||
hooks.each do |hook|
|
||||
hook.call(seq)
|
||||
end
|
||||
end
|
||||
|
||||
# Return the sequence
|
||||
seq
|
||||
# Return the environment in case there are things in there that
|
||||
# the caller wants to use.
|
||||
environment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module VM
|
||||
# Destroys the unused host only interfaces. This middleware cleans
|
||||
# up any created host only networks.
|
||||
class DestroyUnusedNetworkInterfaces
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:vm].driver.delete_unused_host_only_networks
|
||||
|
||||
# Continue along
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module VM
|
||||
# Discards the saved state of the VM if its saved. If its
|
||||
# not saved, does nothing.
|
||||
class DiscardState
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:vm].state == :saved
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding")
|
||||
env[:vm].driver.discard_saved_state
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
module Vagrant
|
||||
module Action
|
||||
module VM
|
||||
class ProvisionerCleanup
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
enabled_provisioners.each do |instance|
|
||||
instance.cleanup
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def enabled_provisioners
|
||||
@env[:vm].config.vm.provisioners.map do |provisioner|
|
||||
provisioner.provisioner.new(@env, provisioner.config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
module Vagrant
|
||||
module Communication
|
||||
autoload :Base, 'vagrant/communication/base'
|
||||
|
||||
autoload :SSH, 'vagrant/communication/ssh'
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
module Vagrant
|
||||
module Communication
|
||||
# The base class for any classes that provide an API for communicating
|
||||
# with the virtual machine.
|
||||
#
|
||||
# There are various stages that require Vagrant to copy files or
|
||||
# run commands on the target system, and communication classes provide
|
||||
# the abstraction necessary to perform these tasks, via SSH or some
|
||||
# other mechanism.
|
||||
#
|
||||
# Any subclasses of this class **must** implement all of the methods
|
||||
# below.
|
||||
class Base
|
||||
# Checks if the target machine is ready for communication.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def ready?
|
||||
end
|
||||
|
||||
# Download a file from the virtual machine to the local machine.
|
||||
#
|
||||
# @param [String] from Path of the file on the virtual machine.
|
||||
# @param [String] to Path to where to save the remote file.
|
||||
def download(from, to)
|
||||
end
|
||||
|
||||
# Upload a file to the virtual machine.
|
||||
#
|
||||
# @param [String] from Path to a file to upload.
|
||||
# @param [String] to Path to where to save this file.
|
||||
def upload(from, to)
|
||||
end
|
||||
|
||||
# Execute a command on the remote machine.
|
||||
#
|
||||
# @param [String] command Command to execute.
|
||||
# @yield [type, data] Realtime output of the command being executed.
|
||||
# @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc.
|
||||
# @yieldparam [String] data Data for the given output.
|
||||
# @return [Integer] Exit code of the command.
|
||||
def execute(command, opts=nil)
|
||||
end
|
||||
|
||||
# Execute a comand with super user privileges.
|
||||
#
|
||||
# See #execute for parameter information.
|
||||
def sudo(command, opts=nil)
|
||||
end
|
||||
|
||||
# Executes a command and returns a boolean statement if it was successful
|
||||
# or not.
|
||||
#
|
||||
# This is implemented by default as expecting `execute` to return 0.
|
||||
def test(command, opts=nil)
|
||||
# Disable error checking no matter what
|
||||
opts = (opts || {}).merge(:error_check => false)
|
||||
|
||||
# Successful if the exit status is 0
|
||||
execute(command, opts) == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,9 +14,6 @@ module Vagrant
|
|||
# handle.
|
||||
def self.match?(url); false; end
|
||||
|
||||
# Called prior to execution so any error checks can be done
|
||||
def prepare(source_url); end
|
||||
|
||||
# Downloads the source file to the destination file. It is up to
|
||||
# implementors of this class to handle the logic.
|
||||
def download!(source_url, destination_file); end
|
||||
|
|
|
@ -9,11 +9,9 @@ module Vagrant
|
|||
::File.file?(::File.expand_path(uri))
|
||||
end
|
||||
|
||||
def prepare(source_url)
|
||||
raise Errors::DownloaderFileDoesntExist if !::File.file?(::File.expand_path(source_url))
|
||||
end
|
||||
|
||||
def download!(source_url, destination_file)
|
||||
raise Errors::DownloaderFileDoesntExist if !::File.file?(::File.expand_path(source_url))
|
||||
|
||||
@ui.info I18n.t("vagrant.downloaders.file.download")
|
||||
FileUtils.cp(::File.expand_path(source_url), destination_file.path)
|
||||
end
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
module Vagrant
|
||||
module Driver
|
||||
autoload :VirtualBox, 'vagrant/driver/virtualbox'
|
||||
autoload :VirtualBox_4_0, 'vagrant/driver/virtualbox_4_0'
|
||||
autoload :VirtualBox_4_1, 'vagrant/driver/virtualbox_4_1'
|
||||
end
|
||||
end
|
|
@ -1,140 +0,0 @@
|
|||
require 'forwardable'
|
||||
|
||||
require 'log4r'
|
||||
|
||||
require 'vagrant/driver/virtualbox_base'
|
||||
|
||||
module Vagrant
|
||||
module Driver
|
||||
# This class contains the logic to drive VirtualBox.
|
||||
#
|
||||
# Read the VirtualBoxBase source for documentation on each method.
|
||||
class VirtualBox < VirtualBoxBase
|
||||
# This is raised if the VM is not found when initializing a driver
|
||||
# with a UUID.
|
||||
class VMNotFound < StandardError; end
|
||||
|
||||
# We use forwardable to do all our driver forwarding
|
||||
extend Forwardable
|
||||
|
||||
# The UUID of the virtual machine we represent
|
||||
attr_reader :uuid
|
||||
|
||||
# The version of virtualbox that is running.
|
||||
attr_reader :version
|
||||
|
||||
def initialize(uuid=nil)
|
||||
# Setup the base
|
||||
super()
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox")
|
||||
@uuid = uuid
|
||||
|
||||
# Read and assign the version of VirtualBox we know which
|
||||
# specific driver to instantiate.
|
||||
begin
|
||||
@version = read_version || ""
|
||||
rescue Subprocess::LaunchError
|
||||
# This means that VirtualBox was not found, so we raise this
|
||||
# error here.
|
||||
raise Errors::VirtualBoxNotDetected
|
||||
end
|
||||
|
||||
# Instantiate the proper version driver for VirtualBox
|
||||
@logger.debug("Finding driver for VirtualBox version: #{@version}")
|
||||
driver_map = {
|
||||
"4.0" => VirtualBox_4_0,
|
||||
"4.1" => VirtualBox_4_1
|
||||
}
|
||||
|
||||
driver_klass = nil
|
||||
driver_map.each do |key, klass|
|
||||
if @version.start_with?(key)
|
||||
driver_klass = klass
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if !driver_klass
|
||||
supported_versions = driver_map.keys.sort.join(", ")
|
||||
raise Errors::VirtualBoxInvalidVersion, :supported_versions => supported_versions
|
||||
end
|
||||
|
||||
@logger.info("Using VirtualBox driver: #{driver_klass}")
|
||||
@driver = driver_klass.new(@uuid)
|
||||
|
||||
if @uuid
|
||||
# Verify the VM exists, and if it doesn't, then don't worry
|
||||
# about it (mark the UUID as nil)
|
||||
raise VMNotFound if !@driver.vm_exists?(@uuid)
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :@driver, :clear_forwarded_ports,
|
||||
:clear_shared_folders,
|
||||
:create_dhcp_server,
|
||||
:create_host_only_network,
|
||||
:delete,
|
||||
:delete_unused_host_only_networks,
|
||||
:discard_saved_state,
|
||||
:enable_adapters,
|
||||
:execute_command,
|
||||
:export,
|
||||
:forward_ports,
|
||||
:halt,
|
||||
:import,
|
||||
:read_forwarded_ports,
|
||||
:read_bridged_interfaces,
|
||||
:read_guest_additions_version,
|
||||
:read_host_only_interfaces,
|
||||
:read_mac_address,
|
||||
:read_mac_addresses,
|
||||
:read_machine_folder,
|
||||
:read_network_interfaces,
|
||||
:read_state,
|
||||
:read_used_ports,
|
||||
:read_vms,
|
||||
:set_mac_address,
|
||||
:set_name,
|
||||
:share_folders,
|
||||
:ssh_port,
|
||||
:start,
|
||||
:suspend,
|
||||
:verify!,
|
||||
:verify_image,
|
||||
:vm_exists?
|
||||
|
||||
protected
|
||||
|
||||
# This returns the version of VirtualBox that is running.
|
||||
#
|
||||
# @return [String]
|
||||
def read_version
|
||||
# The version string is usually in one of the following formats:
|
||||
#
|
||||
# * 4.1.8r1234
|
||||
# * 4.1.8r1234_OSE
|
||||
# * 4.1.8_MacPortsr1234
|
||||
#
|
||||
# Below accounts for all of these.
|
||||
|
||||
# Note: We split this into multiple lines because apparently "".split("_")
|
||||
# is [], so we have to check for an empty array in between.
|
||||
output = execute("--version")
|
||||
if output =~ /vboxdrv kernel module is not loaded/
|
||||
raise Errors::VirtualBoxKernelModuleNotLoaded
|
||||
elsif output =~ /Please install/
|
||||
# Check for installation incomplete warnings, for example:
|
||||
# "WARNING: The character device /dev/vboxdrv does not
|
||||
# exist. Please install the virtualbox-ose-dkms package and
|
||||
# the appropriate headers, most likely linux-headers-generic."
|
||||
raise Errors::VirtualBoxInstallIncomplete
|
||||
end
|
||||
|
||||
parts = output.split("_")
|
||||
return nil if parts.empty?
|
||||
parts[0].split("r")[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,474 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/driver/virtualbox_base'
|
||||
|
||||
module Vagrant
|
||||
module Driver
|
||||
# Driver for VirtualBox 4.0.x
|
||||
class VirtualBox_4_0 < VirtualBoxBase
|
||||
def initialize(uuid)
|
||||
super()
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox_4_0")
|
||||
@uuid = uuid
|
||||
end
|
||||
|
||||
def clear_forwarded_ports
|
||||
args = []
|
||||
read_forwarded_ports(@uuid).each do |nic, name, _, _|
|
||||
args.concat(["--natpf#{nic}", "delete", name])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
|
||||
def clear_shared_folders
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if name = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1]
|
||||
execute("sharedfolder", "remove", @uuid, "--name", name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_dhcp_server(network, options)
|
||||
execute("dhcpserver", "add", "--ifname", network,
|
||||
"--ip", options[:dhcp_ip],
|
||||
"--netmask", options[:netmask],
|
||||
"--lowerip", options[:dhcp_lower],
|
||||
"--upperip", options[:dhcp_upper],
|
||||
"--enable")
|
||||
end
|
||||
|
||||
def create_host_only_network(options)
|
||||
# Create the interface
|
||||
interface = execute("hostonlyif", "create")
|
||||
name = interface[/^Interface '(.+?)' was successfully created$/, 1]
|
||||
|
||||
# Configure it
|
||||
execute("hostonlyif", "ipconfig", name,
|
||||
"--ip", options[:adapter_ip],
|
||||
"--netmask", options[:netmask])
|
||||
|
||||
# Return the details
|
||||
return {
|
||||
:name => name,
|
||||
:ip => options[:adapter_ip],
|
||||
:netmask => options[:netmask],
|
||||
:dhcp => nil
|
||||
}
|
||||
end
|
||||
|
||||
def delete
|
||||
execute("unregistervm", @uuid, "--delete")
|
||||
end
|
||||
|
||||
def delete_unused_host_only_networks
|
||||
networks = []
|
||||
execute("list", "hostonlyifs").split("\n").each do |line|
|
||||
if network_name = line[/^Name:\s+(.+?)$/, 1]
|
||||
networks << network_name
|
||||
end
|
||||
end
|
||||
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
if vm_name = line[/^".+?"\s+\{(.+?)\}$/, 1]
|
||||
info = execute("showvminfo", vm_name, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if network_name = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(network_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
networks.each do |name|
|
||||
# First try to remove any DHCP servers attached. We use `raw` because
|
||||
# it is okay if this fails. It usually means that a DHCP server was
|
||||
# never attached.
|
||||
raw("dhcpserver", "remove", "--ifname", name)
|
||||
|
||||
# Delete the actual host only network interface.
|
||||
execute("hostonlyif", "remove", name)
|
||||
end
|
||||
end
|
||||
|
||||
def discard_saved_state
|
||||
execute("discardstate", @uuid)
|
||||
end
|
||||
|
||||
def enable_adapters(adapters)
|
||||
args = []
|
||||
adapters.each do |adapter|
|
||||
args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s])
|
||||
|
||||
if adapter[:bridge]
|
||||
args.concat(["--bridgeadapter#{adapter[:adapter]}",
|
||||
adapter[:bridge]])
|
||||
end
|
||||
|
||||
if adapter[:hostonly]
|
||||
args.concat(["--hostonlyadapter#{adapter[:adapter]}",
|
||||
adapter[:hostonly]])
|
||||
end
|
||||
|
||||
if adapter[:mac_address]
|
||||
args.concat(["--macaddress#{adapter[:adapter]}",
|
||||
adapter[:mac_address]])
|
||||
end
|
||||
|
||||
if adapter[:nic_type]
|
||||
args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s])
|
||||
end
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args)
|
||||
end
|
||||
|
||||
def execute_command(command)
|
||||
raw(*command)
|
||||
end
|
||||
|
||||
def export(path)
|
||||
# TODO: Progress
|
||||
execute("export", @uuid, "--output", path.to_s)
|
||||
end
|
||||
|
||||
def forward_ports(ports)
|
||||
args = []
|
||||
ports.each do |options|
|
||||
pf_builder = [options[:name],
|
||||
options[:protocol] || "tcp",
|
||||
"",
|
||||
options[:hostport],
|
||||
"",
|
||||
options[:guestport]]
|
||||
|
||||
args.concat(["--natpf#{options[:adapter] || 1}",
|
||||
pf_builder.join(",")])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
|
||||
def halt
|
||||
execute("controlvm", @uuid, "poweroff")
|
||||
end
|
||||
|
||||
def import(ovf)
|
||||
output = ""
|
||||
total = ""
|
||||
last = 0
|
||||
execute("import", ovf) do |type, data|
|
||||
if type == :stdout
|
||||
# Keep track of the stdout so that we can get the VM name
|
||||
output << data
|
||||
elsif type == :stderr
|
||||
# Append the data so we can see the full view
|
||||
total << data
|
||||
|
||||
# Break up the lines. We can't get the progress until we see an "OK"
|
||||
lines = total.split("\n")
|
||||
if lines.include?("OK.")
|
||||
# The progress of the import will be in the last line. Do a greedy
|
||||
# regular expression to find what we're looking for.
|
||||
if current = lines.last[/.+(\d{2})%/, 1]
|
||||
current = current.to_i
|
||||
if current > last
|
||||
last = current
|
||||
yield current if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Find the name of the VM name
|
||||
name = output[/Suggested VM name "(.+?)"/, 1]
|
||||
if !name
|
||||
@logger.error("Couldn't find VM name in the output.")
|
||||
return nil
|
||||
end
|
||||
|
||||
output = execute("list", "vms")
|
||||
if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1]
|
||||
return existing_vm
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
uuid ||= @uuid
|
||||
|
||||
@logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}")
|
||||
|
||||
results = []
|
||||
current_nic = nil
|
||||
info = execute("showvminfo", uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
# This is how we find the nic that a FP is attached to,
|
||||
# since this comes first.
|
||||
if nic = line[/^nic(\d+)=".+?"$/, 1]
|
||||
current_nic = nic.to_i
|
||||
end
|
||||
|
||||
# If we care about active VMs only, then we check the state
|
||||
# to verify the VM is running.
|
||||
if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running")
|
||||
return []
|
||||
end
|
||||
|
||||
# Parse out the forwarded port information
|
||||
if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line)
|
||||
result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i]
|
||||
@logger.debug(" - #{result.inspect}")
|
||||
results << result
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def read_bridged_interfaces
|
||||
execute("list", "bridgedifs").split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if name = line[/^Name:\s+(.+?)$/, 1]
|
||||
info[:name] = name
|
||||
elsif ip = line[/^IPAddress:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1]
|
||||
info[:netmask] = netmask
|
||||
elsif status = line[/^Status:\s+(.+?)$/, 1]
|
||||
info[:status] = status
|
||||
end
|
||||
end
|
||||
|
||||
# Return the info to build up the results
|
||||
info
|
||||
end
|
||||
end
|
||||
|
||||
def read_guest_additions_version
|
||||
output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version",
|
||||
:retryable => true)
|
||||
if value = output[/^Value: (.+?)$/, 1]
|
||||
# Split the version by _ since some distro versions modify it
|
||||
# to look like this: 4.1.2_ubuntu, and the distro part isn't
|
||||
# too important.
|
||||
return value.split("_").first
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def read_host_only_interfaces
|
||||
dhcp = {}
|
||||
execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1]
|
||||
info[:network] = network
|
||||
elsif ip = line[/^IP:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1]
|
||||
info[:lower] = lower
|
||||
elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1]
|
||||
info[:upper] = upper
|
||||
end
|
||||
end
|
||||
|
||||
# Set the DHCP info
|
||||
dhcp[info[:network]] = info
|
||||
end
|
||||
|
||||
execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if name = line[/^Name:\s+(.+?)$/, 1]
|
||||
info[:name] = name
|
||||
elsif ip = line[/^IPAddress:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1]
|
||||
info[:netmask] = netmask
|
||||
elsif status = line[/^Status:\s+(.+?)$/, 1]
|
||||
info[:status] = status
|
||||
end
|
||||
end
|
||||
|
||||
# Set the DHCP info if it exists
|
||||
info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]]
|
||||
|
||||
info
|
||||
end
|
||||
end
|
||||
|
||||
def read_mac_address
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if mac = line[/^macaddress1="(.+?)"$/, 1]
|
||||
return mac
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_mac_addresses
|
||||
macs = {}
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
mac = matcher[2].to_s
|
||||
macs[adapter] = mac
|
||||
end
|
||||
end
|
||||
macs
|
||||
end
|
||||
|
||||
def read_machine_folder
|
||||
execute("list", "systemproperties", :retryable => true).split("\n").each do |line|
|
||||
if folder = line[/^Default machine folder:\s+(.+?)$/i, 1]
|
||||
return folder
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_network_interfaces
|
||||
nics = {}
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if matcher = /^nic(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
type = matcher[2].to_sym
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:type] = type
|
||||
elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
network = matcher[2].to_s
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:hostonly] = network
|
||||
elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
network = matcher[2].to_s
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:bridge] = network
|
||||
end
|
||||
end
|
||||
|
||||
nics
|
||||
end
|
||||
|
||||
def read_state
|
||||
output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
if output =~ /^name="<inaccessible>"$/
|
||||
return :inaccessible
|
||||
elsif state = output[/^VMState="(.+?)"$/, 1]
|
||||
return state.to_sym
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_used_ports
|
||||
ports = []
|
||||
execute("list", "vms", :retryable => true).split("\n").each do |line|
|
||||
if uuid = line[/^".+?" \{(.+?)\}$/, 1]
|
||||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ports
|
||||
end
|
||||
|
||||
def read_vms
|
||||
results = []
|
||||
execute("list", "vms", :retryable => true).split("\n").each do |line|
|
||||
if vm = line[/^".+?" \{(.+?)\}$/, 1]
|
||||
results << vm
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def set_mac_address(mac)
|
||||
execute("modifyvm", @uuid, "--macaddress1", mac)
|
||||
end
|
||||
|
||||
def set_name(name)
|
||||
execute("modifyvm", @uuid, "--name", name)
|
||||
end
|
||||
|
||||
def share_folders(folders)
|
||||
folders.each do |folder|
|
||||
args = ["--name",
|
||||
folder[:name],
|
||||
"--hostpath",
|
||||
folder[:hostpath]]
|
||||
args << "--transient" if folder.has_key?(:transient) && folder[:transient]
|
||||
execute("sharedfolder", "add", @uuid, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def ssh_port(expected_port)
|
||||
@logger.debug("Searching for SSH port: #{expected_port.inspect}")
|
||||
|
||||
# Look for the forwarded port only by comparing the guest port
|
||||
read_forwarded_ports.each do |_, _, hostport, guestport|
|
||||
return hostport if guestport == expected_port
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def start(mode)
|
||||
command = ["startvm", @uuid, "--type", mode.to_s]
|
||||
r = raw(*command)
|
||||
|
||||
if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/
|
||||
# Some systems return an exit code 1 for some reason. For that
|
||||
# we depend on the output.
|
||||
return true
|
||||
end
|
||||
|
||||
# If we reached this point then it didn't work out.
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
|
||||
def suspend
|
||||
execute("controlvm", @uuid, "savestate")
|
||||
end
|
||||
|
||||
def verify!
|
||||
# This command sometimes fails if kernel drivers aren't properly loaded
|
||||
# so we just run the command and verify that it succeeded.
|
||||
execute("list", "hostonlyifs")
|
||||
end
|
||||
|
||||
def verify_image(path)
|
||||
r = raw("import", path.to_s, "--dry-run")
|
||||
return r.exit_code == 0
|
||||
end
|
||||
|
||||
def vm_exists?(uuid)
|
||||
raw("showvminfo", uuid).exit_code == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,474 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/driver/virtualbox_base'
|
||||
|
||||
module Vagrant
|
||||
module Driver
|
||||
# Driver for VirtualBox 4.1.x
|
||||
class VirtualBox_4_1 < VirtualBoxBase
|
||||
def initialize(uuid)
|
||||
super()
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox_4_1")
|
||||
@uuid = uuid
|
||||
end
|
||||
|
||||
def clear_forwarded_ports
|
||||
args = []
|
||||
read_forwarded_ports(@uuid).each do |nic, name, _, _|
|
||||
args.concat(["--natpf#{nic}", "delete", name])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
|
||||
def clear_shared_folders
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if folder = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1]
|
||||
execute("sharedfolder", "remove", @uuid, "--name", folder)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_dhcp_server(network, options)
|
||||
execute("dhcpserver", "add", "--ifname", network,
|
||||
"--ip", options[:dhcp_ip],
|
||||
"--netmask", options[:netmask],
|
||||
"--lowerip", options[:dhcp_lower],
|
||||
"--upperip", options[:dhcp_upper],
|
||||
"--enable")
|
||||
end
|
||||
|
||||
def create_host_only_network(options)
|
||||
# Create the interface
|
||||
interface = execute("hostonlyif", "create")
|
||||
name = interface[/^Interface '(.+?)' was successfully created$/, 1]
|
||||
|
||||
# Configure it
|
||||
execute("hostonlyif", "ipconfig", name,
|
||||
"--ip", options[:adapter_ip],
|
||||
"--netmask", options[:netmask])
|
||||
|
||||
# Return the details
|
||||
return {
|
||||
:name => name,
|
||||
:ip => options[:adapter_ip],
|
||||
:netmask => options[:netmask],
|
||||
:dhcp => nil
|
||||
}
|
||||
end
|
||||
|
||||
def delete
|
||||
execute("unregistervm", @uuid, "--delete")
|
||||
end
|
||||
|
||||
def delete_unused_host_only_networks
|
||||
networks = []
|
||||
execute("list", "hostonlyifs").split("\n").each do |line|
|
||||
if network = line[/^Name:\s+(.+?)$/, 1]
|
||||
networks << network
|
||||
end
|
||||
end
|
||||
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
if vm = line[/^".+?"\s+\{(.+?)\}$/, 1]
|
||||
info = execute("showvminfo", vm, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if adapter = line[/^hostonlyadapter\d+="(.+?)"$/, 1]
|
||||
networks.delete(adapter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
networks.each do |name|
|
||||
# First try to remove any DHCP servers attached. We use `raw` because
|
||||
# it is okay if this fails. It usually means that a DHCP server was
|
||||
# never attached.
|
||||
raw("dhcpserver", "remove", "--ifname", name)
|
||||
|
||||
# Delete the actual host only network interface.
|
||||
execute("hostonlyif", "remove", name)
|
||||
end
|
||||
end
|
||||
|
||||
def discard_saved_state
|
||||
execute("discardstate", @uuid)
|
||||
end
|
||||
|
||||
def enable_adapters(adapters)
|
||||
args = []
|
||||
adapters.each do |adapter|
|
||||
args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s])
|
||||
|
||||
if adapter[:bridge]
|
||||
args.concat(["--bridgeadapter#{adapter[:adapter]}",
|
||||
adapter[:bridge]])
|
||||
end
|
||||
|
||||
if adapter[:hostonly]
|
||||
args.concat(["--hostonlyadapter#{adapter[:adapter]}",
|
||||
adapter[:hostonly]])
|
||||
end
|
||||
|
||||
if adapter[:mac_address]
|
||||
args.concat(["--macaddress#{adapter[:adapter]}",
|
||||
adapter[:mac_address]])
|
||||
end
|
||||
|
||||
if adapter[:nic_type]
|
||||
args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s])
|
||||
end
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args)
|
||||
end
|
||||
|
||||
def execute_command(command)
|
||||
raw(*command)
|
||||
end
|
||||
|
||||
def export(path)
|
||||
# TODO: Progress
|
||||
execute("export", @uuid, "--output", path.to_s)
|
||||
end
|
||||
|
||||
def forward_ports(ports)
|
||||
args = []
|
||||
ports.each do |options|
|
||||
pf_builder = [options[:name],
|
||||
options[:protocol] || "tcp",
|
||||
"",
|
||||
options[:hostport],
|
||||
"",
|
||||
options[:guestport]]
|
||||
|
||||
args.concat(["--natpf#{options[:adapter] || 1}",
|
||||
pf_builder.join(",")])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
|
||||
def halt
|
||||
execute("controlvm", @uuid, "poweroff")
|
||||
end
|
||||
|
||||
def import(ovf)
|
||||
output = ""
|
||||
total = ""
|
||||
last = 0
|
||||
execute("import", ovf) do |type, data|
|
||||
if type == :stdout
|
||||
# Keep track of the stdout so that we can get the VM name
|
||||
output << data
|
||||
elsif type == :stderr
|
||||
# Append the data so we can see the full view
|
||||
total << data
|
||||
|
||||
# Break up the lines. We can't get the progress until we see an "OK"
|
||||
lines = total.split("\n")
|
||||
if lines.include?("OK.")
|
||||
# The progress of the import will be in the last line. Do a greedy
|
||||
# regular expression to find what we're looking for.
|
||||
if current = lines.last[/.+(\d{2})%/, 1]
|
||||
current = current.to_i
|
||||
if current > last
|
||||
last = current
|
||||
yield current if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Find the name of the VM name
|
||||
name = output[/Suggested VM name "(.+?)"/, 1]
|
||||
if !name
|
||||
@logger.error("Couldn't find VM name in the output.")
|
||||
return nil
|
||||
end
|
||||
|
||||
output = execute("list", "vms")
|
||||
if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1]
|
||||
return existing_vm
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
uuid ||= @uuid
|
||||
|
||||
@logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}")
|
||||
|
||||
results = []
|
||||
current_nic = nil
|
||||
info = execute("showvminfo", uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
# This is how we find the nic that a FP is attached to,
|
||||
# since this comes first.
|
||||
if nic = line[/^nic(\d+)=".+?"$/, 1]
|
||||
current_nic = nic.to_i
|
||||
end
|
||||
|
||||
# If we care about active VMs only, then we check the state
|
||||
# to verify the VM is running.
|
||||
if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running")
|
||||
return []
|
||||
end
|
||||
|
||||
# Parse out the forwarded port information
|
||||
if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line)
|
||||
result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i]
|
||||
@logger.debug(" - #{result.inspect}")
|
||||
results << result
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def read_bridged_interfaces
|
||||
execute("list", "bridgedifs").split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if name = line[/^Name:\s+(.+?)$/, 1]
|
||||
info[:name] = name
|
||||
elsif ip = line[/^IPAddress:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1]
|
||||
info[:netmask] = netmask
|
||||
elsif status = line[/^Status:\s+(.+?)$/, 1]
|
||||
info[:status] = status
|
||||
end
|
||||
end
|
||||
|
||||
# Return the info to build up the results
|
||||
info
|
||||
end
|
||||
end
|
||||
|
||||
def read_guest_additions_version
|
||||
output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version",
|
||||
:retryable => true)
|
||||
if value = output[/^Value: (.+?)$/, 1]
|
||||
# Split the version by _ since some distro versions modify it
|
||||
# to look like this: 4.1.2_ubuntu, and the distro part isn't
|
||||
# too important.
|
||||
return value.split("_").first
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def read_host_only_interfaces
|
||||
dhcp = {}
|
||||
execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1]
|
||||
info[:network] = network
|
||||
elsif ip = line[/^IP:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1]
|
||||
info[:lower] = lower
|
||||
elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1]
|
||||
info[:upper] = upper
|
||||
end
|
||||
end
|
||||
|
||||
# Set the DHCP info
|
||||
dhcp[info[:network]] = info
|
||||
end
|
||||
|
||||
execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
||||
block.split("\n").each do |line|
|
||||
if name = line[/^Name:\s+(.+?)$/, 1]
|
||||
info[:name] = name
|
||||
elsif ip = line[/^IPAddress:\s+(.+?)$/, 1]
|
||||
info[:ip] = ip
|
||||
elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1]
|
||||
info[:netmask] = netmask
|
||||
elsif status = line[/^Status:\s+(.+?)$/, 1]
|
||||
info[:status] = status
|
||||
end
|
||||
end
|
||||
|
||||
# Set the DHCP info if it exists
|
||||
info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]]
|
||||
|
||||
info
|
||||
end
|
||||
end
|
||||
|
||||
def read_mac_address
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if mac = line[/^macaddress1="(.+?)"$/, 1]
|
||||
return mac
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_mac_addresses
|
||||
macs = {}
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
mac = matcher[2].to_s
|
||||
macs[adapter] = mac
|
||||
end
|
||||
end
|
||||
macs
|
||||
end
|
||||
|
||||
def read_machine_folder
|
||||
execute("list", "systemproperties", :retryable => true).split("\n").each do |line|
|
||||
if folder = line[/^Default machine folder:\s+(.+?)$/i, 1]
|
||||
return folder
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_network_interfaces
|
||||
nics = {}
|
||||
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if matcher = /^nic(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
type = matcher[2].to_sym
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:type] = type
|
||||
elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
network = matcher[2].to_s
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:hostonly] = network
|
||||
elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line)
|
||||
adapter = matcher[1].to_i
|
||||
network = matcher[2].to_s
|
||||
|
||||
nics[adapter] ||= {}
|
||||
nics[adapter][:bridge] = network
|
||||
end
|
||||
end
|
||||
|
||||
nics
|
||||
end
|
||||
|
||||
def read_state
|
||||
output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
|
||||
if output =~ /^name="<inaccessible>"$/
|
||||
return :inaccessible
|
||||
elsif state = output[/^VMState="(.+?)"$/, 1]
|
||||
return state.to_sym
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def read_used_ports
|
||||
ports = []
|
||||
execute("list", "vms", :retryable => true).split("\n").each do |line|
|
||||
if uuid = line[/^".+?" \{(.+?)\}$/, 1]
|
||||
# Ignore our own used ports
|
||||
next if uuid == @uuid
|
||||
|
||||
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
|
||||
ports << hostport
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ports
|
||||
end
|
||||
|
||||
def read_vms
|
||||
results = []
|
||||
execute("list", "vms", :retryable => true).split("\n").each do |line|
|
||||
if vm = line[/^".+?" \{(.+?)\}$/, 1]
|
||||
results << vm
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def set_mac_address(mac)
|
||||
execute("modifyvm", @uuid, "--macaddress1", mac)
|
||||
end
|
||||
|
||||
def set_name(name)
|
||||
execute("modifyvm", @uuid, "--name", name)
|
||||
end
|
||||
|
||||
def share_folders(folders)
|
||||
folders.each do |folder|
|
||||
args = ["--name",
|
||||
folder[:name],
|
||||
"--hostpath",
|
||||
folder[:hostpath]]
|
||||
args << "--transient" if folder.has_key?(:transient) && folder[:transient]
|
||||
execute("sharedfolder", "add", @uuid, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def ssh_port(expected_port)
|
||||
@logger.debug("Searching for SSH port: #{expected_port.inspect}")
|
||||
|
||||
# Look for the forwarded port only by comparing the guest port
|
||||
read_forwarded_ports.each do |_, _, hostport, guestport|
|
||||
return hostport if guestport == expected_port
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def start(mode)
|
||||
command = ["startvm", @uuid, "--type", mode.to_s]
|
||||
r = raw(*command)
|
||||
|
||||
if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/
|
||||
# Some systems return an exit code 1 for some reason. For that
|
||||
# we depend on the output.
|
||||
return true
|
||||
end
|
||||
|
||||
# If we reached this point then it didn't work out.
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
|
||||
def suspend
|
||||
execute("controlvm", @uuid, "savestate")
|
||||
end
|
||||
|
||||
def verify!
|
||||
# This command sometimes fails if kernel drivers aren't properly loaded
|
||||
# so we just run the command and verify that it succeeded.
|
||||
execute("list", "hostonlyifs")
|
||||
end
|
||||
|
||||
def verify_image(path)
|
||||
r = raw("import", path.to_s, "--dry-run")
|
||||
return r.exit_code == 0
|
||||
end
|
||||
|
||||
def vm_exists?(uuid)
|
||||
raw("showvminfo", uuid).exit_code == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,326 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/util/busy'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/retryable'
|
||||
require 'vagrant/util/subprocess'
|
||||
|
||||
module Vagrant
|
||||
module Driver
|
||||
# Base class for all VirtualBox drivers.
|
||||
#
|
||||
# This class provides useful tools for things such as executing
|
||||
# VBoxManage and handling SIGINTs and so on.
|
||||
class VirtualBoxBase
|
||||
# Include this so we can use `Subprocess` more easily.
|
||||
include Vagrant::Util
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def initialize
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox_base")
|
||||
|
||||
# This flag is used to keep track of interrupted state (SIGINT)
|
||||
@interrupted = false
|
||||
|
||||
# Set the path to VBoxManage
|
||||
@vboxmanage_path = "VBoxManage"
|
||||
|
||||
if Util::Platform.windows?
|
||||
@logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage")
|
||||
|
||||
# On Windows, we use the VBOX_INSTALL_PATH environmental
|
||||
# variable to find VBoxManage.
|
||||
if ENV.has_key?("VBOX_INSTALL_PATH")
|
||||
# Get the path.
|
||||
path = ENV["VBOX_INSTALL_PATH"]
|
||||
@logger.debug("VBOX_INSTALL_PATH value: #{path}")
|
||||
|
||||
# There can actually be multiple paths in here, so we need to
|
||||
# split by the separator ";" and see which is a good one.
|
||||
path.split(";").each do |single|
|
||||
# Make sure it ends with a \
|
||||
single += "\\" if !single.end_with?("\\")
|
||||
|
||||
# If the executable exists, then set it as the main path
|
||||
# and break out
|
||||
vboxmanage = "#{path}VBoxManage.exe"
|
||||
if File.file?(vboxmanage)
|
||||
@vboxmanage_path = vboxmanage
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@logger.info("VBoxManage path: #{@vboxmanage_path}")
|
||||
end
|
||||
|
||||
# Clears the forwarded ports that have been set on the virtual machine.
|
||||
def clear_forwarded_ports
|
||||
end
|
||||
|
||||
# Clears the shared folders that have been set on the virtual machine.
|
||||
def clear_shared_folders
|
||||
end
|
||||
|
||||
# Creates a DHCP server for a host only network.
|
||||
#
|
||||
# @param [String] network Name of the host-only network.
|
||||
# @param [Hash] options Options for the DHCP server.
|
||||
def create_dhcp_server(network, options)
|
||||
end
|
||||
|
||||
# Creates a host only network with the given options.
|
||||
#
|
||||
# @param [Hash] options Options to create the host only network.
|
||||
# @return [Hash] The details of the host only network, including
|
||||
# keys `:name`, `:ip`, and `:netmask`
|
||||
def create_host_only_network(options)
|
||||
end
|
||||
|
||||
# Deletes the virtual machine references by this driver.
|
||||
def delete
|
||||
end
|
||||
|
||||
# Deletes any host only networks that aren't being used for anything.
|
||||
def delete_unused_host_only_networks
|
||||
end
|
||||
|
||||
# Discards any saved state associated with this VM.
|
||||
def discard_saved_state
|
||||
end
|
||||
|
||||
# Enables network adapters on the VM.
|
||||
#
|
||||
# The format of each adapter specification should be like so:
|
||||
#
|
||||
# {
|
||||
# :type => :hostonly,
|
||||
# :hostonly => "vboxnet0",
|
||||
# :mac_address => "tubes"
|
||||
# }
|
||||
#
|
||||
# This must support setting up both host only and bridged networks.
|
||||
#
|
||||
# @param [Array<Hash>] adapters Array of adapters to enable.
|
||||
def enable_adapters(adapters)
|
||||
end
|
||||
|
||||
# Execute a raw command straight through to VBoxManage.
|
||||
#
|
||||
# @param [Array] command Command to execute.
|
||||
def execute_command(command)
|
||||
end
|
||||
|
||||
# Exports the virtual machine to the given path.
|
||||
#
|
||||
# @param [String] path Path to the OVF file.
|
||||
# @yield [progress] Yields the block with the progress of the export.
|
||||
def export(path)
|
||||
end
|
||||
|
||||
# Forwards a set of ports for a VM.
|
||||
#
|
||||
# This will not affect any previously set forwarded ports,
|
||||
# so be sure to delete those if you need to.
|
||||
#
|
||||
# The format of each port hash should be the following:
|
||||
#
|
||||
# {
|
||||
# :name => "foo",
|
||||
# :hostport => 8500,
|
||||
# :guestport => 80,
|
||||
# :adapter => 1,
|
||||
# :protocol => "tcp"
|
||||
# }
|
||||
#
|
||||
# Note that "adapter" and "protocol" are optional and will default
|
||||
# to 1 and "tcp" respectively.
|
||||
#
|
||||
# @param [Array<Hash>] ports An array of ports to set. See documentation
|
||||
# for more information on the format.
|
||||
def forward_ports(ports)
|
||||
end
|
||||
|
||||
# Halts the virtual machine (pulls the plug).
|
||||
def halt
|
||||
end
|
||||
|
||||
# Imports the VM from an OVF file.
|
||||
#
|
||||
# @param [String] ovf Path to the OVF file.
|
||||
# @return [String] UUID of the imported VM.
|
||||
def import(ovf)
|
||||
end
|
||||
|
||||
# Returns a list of forwarded ports for a VM.
|
||||
#
|
||||
# @param [String] uuid UUID of the VM to read from, or `nil` if this
|
||||
# VM.
|
||||
# @param [Boolean] active_only If true, only VMs that are running will
|
||||
# be checked.
|
||||
# @return [Array<Array>]
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
end
|
||||
|
||||
# Returns a list of bridged interfaces.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_bridged_interfaces
|
||||
end
|
||||
|
||||
# Returns the guest additions version that is installed on this VM.
|
||||
#
|
||||
# @return [String]
|
||||
def read_guest_additions_version
|
||||
end
|
||||
|
||||
# Returns a list of available host only interfaces.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_host_only_interfaces
|
||||
end
|
||||
|
||||
# Returns the MAC address of the first network interface.
|
||||
#
|
||||
# @return [String]
|
||||
def read_mac_address
|
||||
end
|
||||
|
||||
# Returns the folder where VirtualBox places it's VMs.
|
||||
#
|
||||
# @return [String]
|
||||
def read_machine_folder
|
||||
end
|
||||
|
||||
# Returns a list of network interfaces of the VM.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_network_interfaces
|
||||
end
|
||||
|
||||
# Returns the current state of this VM.
|
||||
#
|
||||
# @return [Symbol]
|
||||
def read_state
|
||||
end
|
||||
|
||||
# Returns a list of all forwarded ports in use by active
|
||||
# virtual machines.
|
||||
#
|
||||
# @return [Array]
|
||||
def read_used_ports
|
||||
end
|
||||
|
||||
# Returns a list of all UUIDs of virtual machines currently
|
||||
# known by VirtualBox.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
def read_vms
|
||||
end
|
||||
|
||||
# Sets the MAC address of the first network adapter.
|
||||
#
|
||||
# @param [String] mac MAC address without any spaces/hyphens.
|
||||
def set_mac_address(mac)
|
||||
end
|
||||
|
||||
# Share a set of folders on this VM.
|
||||
#
|
||||
# @param [Array<Hash>] folders
|
||||
def share_folders(folders)
|
||||
end
|
||||
|
||||
# Reads the SSH port of this VM.
|
||||
#
|
||||
# @param [Integer] expected Expected guest port of SSH.
|
||||
def ssh_port(expected)
|
||||
end
|
||||
|
||||
# Starts the virtual machine.
|
||||
#
|
||||
# @param [String] mode Mode to boot the VM. Either "headless"
|
||||
# or "gui"
|
||||
def start(mode)
|
||||
end
|
||||
|
||||
# Suspend the virtual machine.
|
||||
def suspend
|
||||
end
|
||||
|
||||
# Verifies that the driver is ready to accept work.
|
||||
#
|
||||
# This should raise a VagrantError if things are not ready.
|
||||
def verify!
|
||||
end
|
||||
|
||||
# Verifies that an image can be imported properly.
|
||||
#
|
||||
# @param [String] path Path to an OVF file.
|
||||
# @return [Boolean]
|
||||
def verify_image(path)
|
||||
end
|
||||
|
||||
# Checks if a VM with the given UUID exists.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def vm_exists?(uuid)
|
||||
end
|
||||
|
||||
# Execute the given subcommand for VBoxManage and return the output.
|
||||
def execute(*command, &block)
|
||||
# Get the options hash if it exists
|
||||
opts = {}
|
||||
opts = command.pop if command.last.is_a?(Hash)
|
||||
|
||||
tries = 0
|
||||
tries = 3 if opts[:retryable]
|
||||
|
||||
# Variable to store our execution result
|
||||
r = nil
|
||||
|
||||
retryable(:on => Errors::VBoxManageError, :tries => tries, :sleep => 1) do
|
||||
# Execute the command
|
||||
r = raw(*command, &block)
|
||||
|
||||
# If the command was a failure, then raise an exception that is
|
||||
# nicely handled by Vagrant.
|
||||
if r.exit_code != 0
|
||||
if @interrupted
|
||||
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
||||
else
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
else
|
||||
# Sometimes, VBoxManage fails but doesn't actual return a non-zero
|
||||
# exit code. For this we inspect the output and determine if an error
|
||||
# occurred.
|
||||
if r.stderr =~ /VBoxManage: error:/
|
||||
@logger.info("VBoxManage error text found, assuming error.")
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the output, making sure to replace any Windows-style
|
||||
# newlines with Unix-style.
|
||||
r.stdout.gsub("\r\n", "\n")
|
||||
end
|
||||
|
||||
# Executes a command and returns the raw result object.
|
||||
def raw(*command, &block)
|
||||
int_callback = lambda do
|
||||
@interrupted = true
|
||||
@logger.info("Interrupted.")
|
||||
end
|
||||
|
||||
# Append in the options for subprocess
|
||||
command << { :notify => [:stdout, :stderr] }
|
||||
|
||||
Util::Busy.busy(int_callback) do
|
||||
Subprocess.execute(@vboxmanage_path, *command, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -104,6 +104,14 @@ module Vagrant
|
|||
activate_plugins
|
||||
end
|
||||
|
||||
# Return a human-friendly string for pretty printed or inspected
|
||||
# instances.
|
||||
#
|
||||
# @return [String]
|
||||
def inspect
|
||||
"#<#{self.class}: #{@cwd}>"
|
||||
end
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Helpers
|
||||
#---------------------------------------------------------------
|
||||
|
@ -114,7 +122,7 @@ module Vagrant
|
|||
# @return [Pathname]
|
||||
def dotfile_path
|
||||
return nil if !root_path
|
||||
root_path.join(File.expand_path(config.global.vagrant.dotfile_name))
|
||||
root_path.join(config.global.vagrant.dotfile_name)
|
||||
end
|
||||
|
||||
# Returns the collection of boxes for the environment.
|
||||
|
@ -205,7 +213,7 @@ module Vagrant
|
|||
#
|
||||
# @return [Action::Runner]
|
||||
def action_runner
|
||||
@action_runner ||= Action::Runner.new(action_registry) do
|
||||
@action_runner ||= Action::Runner.new do
|
||||
{
|
||||
:action_runner => action_runner,
|
||||
:box_collection => boxes,
|
||||
|
@ -218,16 +226,6 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# Action registry for registering new actions with this environment.
|
||||
#
|
||||
# @return [Registry]
|
||||
def action_registry
|
||||
# For now we return the global built-in actions registry. In the future
|
||||
# we may want to create an isolated registry that inherits from this
|
||||
# global one, but for now there isn't a use case that calls for it.
|
||||
Vagrant.actions
|
||||
end
|
||||
|
||||
# Loads on initial access and reads data from the global data store.
|
||||
# The global data store is global to Vagrant everywhere (in every environment),
|
||||
# so it can be used to store system-wide information. Note that "system-wide"
|
||||
|
@ -445,11 +443,22 @@ module Vagrant
|
|||
|
||||
# Loads the persisted VM (if it exists) for this environment.
|
||||
def load_vms!
|
||||
result = {}
|
||||
# This is hardcoded for now.
|
||||
provider = nil
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
provider = plugin.provider.get(:virtualbox)
|
||||
break if provider
|
||||
end
|
||||
|
||||
raise "VirtualBox provider not found." if !provider
|
||||
|
||||
# Load all the virtual machine instances.
|
||||
result = {}
|
||||
config.vms.each do |name|
|
||||
result[name] = Vagrant::VM.new(name, self, config.for_vm(name))
|
||||
vm_config = config.for_vm(name)
|
||||
box = boxes.find(vm_config.vm.box, :virtualbox)
|
||||
|
||||
result[name] = Vagrant::Machine.new(name, provider, vm_config, box, self)
|
||||
end
|
||||
|
||||
result
|
||||
|
|
|
@ -226,6 +226,10 @@ module Vagrant
|
|||
error_key(:port_collision_resume)
|
||||
end
|
||||
|
||||
class MachineGuestNotReady < VagrantError
|
||||
error_key(:machine_guest_not_ready)
|
||||
end
|
||||
|
||||
class MultiVMEnvironmentRequired < VagrantError
|
||||
status_code(5)
|
||||
error_key(:multi_vm_required)
|
||||
|
@ -356,6 +360,10 @@ module Vagrant
|
|||
error_key(:ssh_key_type_not_supported)
|
||||
end
|
||||
|
||||
class SSHNotReady < VagrantError
|
||||
error_key(:ssh_not_ready)
|
||||
end
|
||||
|
||||
class SSHPortNotDetected < VagrantError
|
||||
status_code(50)
|
||||
error_key(:ssh_port_not_detected)
|
||||
|
@ -376,6 +384,10 @@ module Vagrant
|
|||
error_key(:ui_expects_tty)
|
||||
end
|
||||
|
||||
class UnimplementedProviderAction < VagrantError
|
||||
error_key(:unimplemented_provider_action)
|
||||
end
|
||||
|
||||
class VagrantInterrupt < VagrantError
|
||||
status_code(40)
|
||||
error_key(:interrupted)
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
# This represents a machine that Vagrant manages. This provides a singular
|
||||
# API for querying the state and making state changes to the machine, which
|
||||
# is backed by any sort of provider (VirtualBox, VMWare, etc.).
|
||||
class Machine
|
||||
# The box that is backing this machine.
|
||||
#
|
||||
# @return [Box]
|
||||
attr_reader :box
|
||||
|
||||
# Configuration for the machine.
|
||||
#
|
||||
# @return [Object]
|
||||
attr_reader :config
|
||||
|
||||
# The environment that this machine is a part of.
|
||||
#
|
||||
# @return [Environment]
|
||||
attr_reader :env
|
||||
|
||||
# ID of the machine. This ID comes from the provider and is not
|
||||
# guaranteed to be of any particular format except that it is
|
||||
# a string.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :id
|
||||
|
||||
# Name of the machine. This is assigned by the Vagrantfile.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :name
|
||||
|
||||
# The provider backing this machine.
|
||||
#
|
||||
# @return [Object]
|
||||
attr_reader :provider
|
||||
|
||||
# Initialize a new machine.
|
||||
#
|
||||
# @param [String] name Name of the virtual machine.
|
||||
# @param [Class] provider The provider backing this machine. This is
|
||||
# currently expected to be a V1 `provider` plugin.
|
||||
# @param [Object] config The configuration for this machine.
|
||||
# @param [Box] box The box that is backing this virtual machine.
|
||||
# @param [Environment] env The environment that this machine is a
|
||||
# part of.
|
||||
def initialize(name, provider_cls, config, box, env, base=false)
|
||||
@logger = Log4r::Logger.new("vagrant::machine")
|
||||
@logger.info("Initializing machine: #{name}")
|
||||
@logger.info(" - Provider: #{provider_cls}")
|
||||
@logger.info(" - Box: #{box}")
|
||||
|
||||
@name = name
|
||||
@box = box
|
||||
@config = config
|
||||
@env = env
|
||||
|
||||
# Read the ID, which is usually in local storage
|
||||
@id = nil
|
||||
|
||||
# XXX: This is temporary. This will be removed very soon.
|
||||
if base
|
||||
@id = name
|
||||
else
|
||||
@id = @env.local_data[:active][@name.to_s] if @env.local_data[:active]
|
||||
end
|
||||
|
||||
# Initializes the provider last so that it has access to all the
|
||||
# state we setup on this machine.
|
||||
@provider = provider_cls.new(self)
|
||||
end
|
||||
|
||||
# This calls an action on the provider. The provider may or may not
|
||||
# actually implement the action.
|
||||
#
|
||||
# @param [Symbol] name Name of the action to run.
|
||||
# @param [Hash] extra_env This data will be passed into the action runner
|
||||
# as extra data set on the environment hash for the middleware
|
||||
# runner.
|
||||
def action(name, extra_env=nil)
|
||||
@logger.debug("Calling action: #{name} on provider #{@provider}")
|
||||
|
||||
# Get the callable from the provider.
|
||||
callable = @provider.action(name)
|
||||
|
||||
# If this action doesn't exist on the provider, then an exception
|
||||
# must be raised.
|
||||
if callable.nil?
|
||||
raise Errors::UnimplementedProviderAction,
|
||||
:action => name,
|
||||
:provider => @provider.to_s
|
||||
end
|
||||
|
||||
# Run the action with the action runner on the environment
|
||||
env = { :machine => self }.merge(extra_env || {})
|
||||
@env.action_runner.run(callable, env)
|
||||
end
|
||||
|
||||
# Returns a communication object for executing commands on the remote
|
||||
# machine. Note that the _exact_ semantics of this are up to the
|
||||
# communication provider itself. Despite this, the semantics are expected
|
||||
# to be consistent across operating systems. For example, all linux-based
|
||||
# systems should have similar communication (usually a shell). All
|
||||
# Windows systems should have similar communication as well. Therefore,
|
||||
# prior to communicating with the machine, users of this method are
|
||||
# expected to check the guest OS to determine their behavior.
|
||||
#
|
||||
# This method will _always_ return some valid communication object.
|
||||
# The `ready?` API can be used on the object to check if communication
|
||||
# is actually ready.
|
||||
#
|
||||
# @return [Object]
|
||||
def communicate
|
||||
if !@communicator
|
||||
# For now, we always return SSH. In the future, we'll abstract
|
||||
# this and allow plugins to define new methods of communication.
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
klass = plugin.communicator[:ssh]
|
||||
if klass
|
||||
# This plugin has the SSH communicator, use it.
|
||||
@communicator = klass.new(self)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@communicator
|
||||
end
|
||||
|
||||
# Returns a guest implementation for this machine. The guest implementation
|
||||
# knows how to do guest-OS specific tasks, such as configuring networks,
|
||||
# mounting folders, etc.
|
||||
#
|
||||
# @return [Object]
|
||||
def guest
|
||||
raise Errors::MachineGuestNotReady if !communicate.ready?
|
||||
|
||||
# Load the initial guest.
|
||||
last_guest = config.vm.guest
|
||||
guest = load_guest(last_guest)
|
||||
|
||||
# Loop and distro dispatch while there are distros.
|
||||
while true
|
||||
distro = guest.distro_dispatch
|
||||
break if !distro
|
||||
|
||||
# This is just some really basic loop detection and avoiding for
|
||||
# guest classes. This is just here to help implementers a bit
|
||||
# avoid a situation that is fairly easy, since if you subclass
|
||||
# a parent which does `distro_dispatch`, you'll end up dispatching
|
||||
# forever.
|
||||
if distro == last_guest
|
||||
@logger.warn("Distro dispatch loop in '#{distro}'. Exiting loop.")
|
||||
break
|
||||
end
|
||||
|
||||
last_guest = distro
|
||||
guest = load_guest(distro)
|
||||
end
|
||||
|
||||
# Return the result
|
||||
guest
|
||||
end
|
||||
|
||||
# This sets the unique ID associated with this machine. This will
|
||||
# persist this ID so that in the future Vagrant will be able to find
|
||||
# this machine again. The unique ID must be absolutely unique to the
|
||||
# virtual machine, and can be used by providers for finding the
|
||||
# actual machine associated with this instance.
|
||||
#
|
||||
# **WARNING:** Only providers should ever use this method.
|
||||
#
|
||||
# @param [String] value The ID.
|
||||
def id=(value)
|
||||
@env.local_data[:active] ||= {}
|
||||
|
||||
if value
|
||||
# Set the value
|
||||
@env.local_data[:active][@name] = value
|
||||
else
|
||||
# Delete it from the active hash
|
||||
@env.local_data[:active].delete(@name)
|
||||
end
|
||||
|
||||
# Commit the local data so that the next time Vagrant is initialized,
|
||||
# it realizes the VM exists (or doesn't).
|
||||
@env.local_data.commit
|
||||
|
||||
# Store the ID locally
|
||||
@id = value
|
||||
|
||||
# Notify the provider that the ID changed in case it needs to do
|
||||
# any accounting from it.
|
||||
@provider.machine_id_changed
|
||||
end
|
||||
|
||||
# This returns a clean inspect value so that printing the value via
|
||||
# a pretty print (`p`) results in a readable value.
|
||||
#
|
||||
# @return [String]
|
||||
def inspect
|
||||
"#<#{self.class}: #{@name} (#{@provider.class})>"
|
||||
end
|
||||
|
||||
# This returns the SSH info for accessing this machine. This SSH info
|
||||
# is queried from the underlying provider. This method returns `nil` if
|
||||
# the machine is not ready for SSH communication.
|
||||
#
|
||||
# The structure of the resulting hash is guaranteed to contain the
|
||||
# following structure, although it may return other keys as well
|
||||
# not documented here:
|
||||
#
|
||||
# {
|
||||
# :host => "1.2.3.4",
|
||||
# :port => "22",
|
||||
# :username => "mitchellh",
|
||||
# :private_key_path => "/path/to/my/key"
|
||||
# }
|
||||
#
|
||||
# Note that Vagrant makes no guarantee that this info works or is
|
||||
# correct. This is simply the data that the provider gives us or that
|
||||
# is configured via a Vagrantfile. It is still possible after this
|
||||
# point when attempting to connect via SSH to get authentication
|
||||
# errors.
|
||||
#
|
||||
# @return [Hash] SSH information.
|
||||
def ssh_info
|
||||
# First, ask the provider for their information. If the provider
|
||||
# returns nil, then the machine is simply not ready for SSH, and
|
||||
# we return nil as well.
|
||||
info = @provider.ssh_info
|
||||
return nil if info.nil?
|
||||
|
||||
# Delete out the nil entries.
|
||||
info.dup.each do |key, value|
|
||||
info.delete(key) if value.nil?
|
||||
end
|
||||
|
||||
# Next, we default some fields if they weren't given to us by
|
||||
# the provider.
|
||||
info[:host] = @config.ssh.host if @config.ssh.host
|
||||
info[:port] = @config.ssh.port if @config.ssh.port
|
||||
info[:username] = @config.ssh.username if @config.ssh.username
|
||||
|
||||
# We also set some fields that are purely controlled by Varant
|
||||
info[:forward_agent] = @config.ssh.forward_agent
|
||||
info[:forward_x11] = @config.ssh.forward_x11
|
||||
|
||||
# Set the private key path. If a specific private key is given in
|
||||
# the Vagrantfile we set that. Otherwise, we use the default (insecure)
|
||||
# private key, but only if the provider didn't give us one.
|
||||
info[:private_key_path] = @config.ssh.private_key_path if @config.ssh.private_key_path
|
||||
info[:private_key_path] = @env.default_private_key_path if !info[:private_key_path]
|
||||
|
||||
# Return the final compiled SSH info data
|
||||
info
|
||||
end
|
||||
|
||||
# Returns the state of this machine. The state is queried from the
|
||||
# backing provider, so it can be any arbitrary symbol.
|
||||
#
|
||||
# @return [Symbol]
|
||||
def state
|
||||
@provider.state
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Given a guest name (such as `:windows`), this will load the associated
|
||||
# guest implementation and return an instance.
|
||||
#
|
||||
# @param [Symbol] guest The name of the guest implementation.
|
||||
# @return [Object]
|
||||
def load_guest(guest)
|
||||
@logger.info("Loading guest: #{guest}")
|
||||
|
||||
klass = nil
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
if plugin.guest.has_key?(guest)
|
||||
klass = plugin.guest[guest]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if klass.nil?
|
||||
raise Errors::VMGuestError,
|
||||
:_key => :unknown_type,
|
||||
:guest => guest.to_s
|
||||
end
|
||||
|
||||
return klass.new(self)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,10 +6,12 @@ module Vagrant
|
|||
module Plugin
|
||||
module V1
|
||||
autoload :Command, "vagrant/plugin/v1/command"
|
||||
autoload :Communicator, "vagrant/plugin/v1/communicator"
|
||||
autoload :Config, "vagrant/plugin/v1/config"
|
||||
autoload :Guest, "vagrant/plugin/v1/guest"
|
||||
autoload :Host, "vagrant/plugin/v1/host"
|
||||
autoload :Plugin, "vagrant/plugin/v1/plugin"
|
||||
autoload :Provider, "vagrant/plugin/v1/provider"
|
||||
autoload :Provisioner, "vagrant/plugin/v1/provisioner"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
module Vagrant
|
||||
module Plugin
|
||||
module V1
|
||||
# Base class for a communicator in Vagrant. A communicator is
|
||||
# responsible for communicating with a machine in some way. There
|
||||
# are various stages of Vagrant that require things such as uploading
|
||||
# files to the machine, executing shell commands, etc. Implementors
|
||||
# of this class are expected to provide this functionality in some
|
||||
# way.
|
||||
#
|
||||
# Note that a communicator must provide **all** of the methods
|
||||
# in this base class. There is currently no way for one communicator
|
||||
# to provide say a more efficient way of uploading a file, but not
|
||||
# provide shell execution. This sort of thing will come in a future
|
||||
# version.
|
||||
class Communicator
|
||||
# This returns true/false depending on if the given machine
|
||||
# can be communicated with using this communicator. If this returns
|
||||
# `true`, then this class will be used as the primary communication
|
||||
# method for the machine.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def self.match?(machine)
|
||||
false
|
||||
end
|
||||
|
||||
# Initializes the communicator with the machine that we will be
|
||||
# communicating with. This base method does nothing (it doesn't
|
||||
# even store the machine in an instance variable for you), so you're
|
||||
# expected to override this and do something with the machine if
|
||||
# you care about it.
|
||||
#
|
||||
# @param [Machine] machine The machine this instance is expected to
|
||||
# communicate with.
|
||||
def initialize(machine)
|
||||
end
|
||||
|
||||
# Checks if the target machine is ready for communication. If this
|
||||
# returns true, then all the other methods for communicating with
|
||||
# the machine are expected to be functional.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def ready?
|
||||
false
|
||||
end
|
||||
|
||||
# Download a file from the remote machine to the local machine.
|
||||
#
|
||||
# @param [String] from Path of the file on the remote machine.
|
||||
# @param [String] to Path of where to save the file locally.
|
||||
def download(from, to)
|
||||
end
|
||||
|
||||
# Upload a file to the remote machine.
|
||||
#
|
||||
# @param [String] from Path of the file locally to upload.
|
||||
# @param [String] to Path of where to save the file on the remote
|
||||
# machine.
|
||||
def upload(from, to)
|
||||
end
|
||||
|
||||
# Execute a command on the remote machine. The exact semantics
|
||||
# of this method are up to the implementor, but in general the
|
||||
# users of this class will expect this to be a shell.
|
||||
#
|
||||
# This method gives you no way to write data back to the remote
|
||||
# machine, so only execute commands that don't expect input.
|
||||
#
|
||||
# @param [String] command Command to execute.
|
||||
# @yield [type, data] Realtime output of the command being executed.
|
||||
# @yieldparam [String] type Type of the output. This can be
|
||||
# `:stdout`, `:stderr`, etc. The exact types are up to the
|
||||
# implementor.
|
||||
# @yieldparam [String] data Data for the given output.
|
||||
# @return [Integer] Exit code of the command.
|
||||
def execute(command, opts=nil)
|
||||
end
|
||||
|
||||
# Executes a command on the remote machine with administrative
|
||||
# privileges. See {#execute} for documentation, as the API is the
|
||||
# same.
|
||||
#
|
||||
# @see #execute
|
||||
def sudo(command, opts=nil)
|
||||
end
|
||||
|
||||
# Executes a command and returns true if the command succeeded,
|
||||
# and false otherwise. By default, this executes as a normal user,
|
||||
# and it is up to the communicator implementation if they expose an
|
||||
# option for running tests as an administrator.
|
||||
#
|
||||
# @see #execute
|
||||
def test(command, opts=nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,9 +27,8 @@ module Vagrant
|
|||
# Vagrant can successfully SSH into the machine) to give the system a chance
|
||||
# to determine the distro and return a distro-specific system.
|
||||
#
|
||||
# **Warning:** If a return value which subclasses from {Base} is
|
||||
# returned, Vagrant will use it as the new system instance for the
|
||||
# class.
|
||||
# If this method returns nil, then this instance is assumed to be
|
||||
# the most specific guest implementation.
|
||||
def distro_dispatch
|
||||
end
|
||||
|
||||
|
|
|
@ -103,6 +103,24 @@ module Vagrant
|
|||
data[:command]
|
||||
end
|
||||
|
||||
# Defines additional communicators to be available. Communicators
|
||||
# should be returned by a block passed to this method. This is done
|
||||
# to ensure that the class is lazy loaded, so if your class inherits
|
||||
# from or uses any Vagrant internals specific to Vagrant 1.0, then
|
||||
# the plugin can still be defined without breaking anything in future
|
||||
# versions of Vagrant.
|
||||
#
|
||||
# @param [String] name Communicator name.
|
||||
def self.communicator(name=UNSET_VALUE, &block)
|
||||
data[:communicator] ||= Registry.new
|
||||
|
||||
# Register a new communicator class only if a name was given.
|
||||
data[:communicator].register(name.to_sym, &block) if name != UNSET_VALUE
|
||||
|
||||
# Return the registry
|
||||
data[:communicator]
|
||||
end
|
||||
|
||||
# Defines additional configuration keys to be available in the
|
||||
# Vagrantfile. The configuration class should be returned by a
|
||||
# block passed to this method. This is done to ensure that the class
|
||||
|
@ -181,6 +199,19 @@ module Vagrant
|
|||
data[:hosts]
|
||||
end
|
||||
|
||||
# Registers additional providers to be available.
|
||||
#
|
||||
# @param [Symbol] name Name of the provider.
|
||||
def self.provider(name=UNSET_VALUE, &block)
|
||||
data[:providers] ||= Registry.new
|
||||
|
||||
# Register a new provider class only if a name was given
|
||||
data[:providers].register(name.to_sym, &block) if name != UNSET_VALUE
|
||||
|
||||
# Return the registry
|
||||
data[:providers]
|
||||
end
|
||||
|
||||
# Registers additional provisioners to be available.
|
||||
#
|
||||
# @param [String] name Name of the provisioner.
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
module Vagrant
|
||||
module Plugin
|
||||
module V1
|
||||
# This is the base class for a provider for the V1 API. A provider
|
||||
# is responsible for creating compute resources to match the needs
|
||||
# of a Vagrant-configured system.
|
||||
class Provider
|
||||
# Initialize the provider to represent the given machine.
|
||||
#
|
||||
# @param [Vagrant::Machine] machine The machine that this provider
|
||||
# is responsible for.
|
||||
def initialize(machine)
|
||||
end
|
||||
|
||||
# This should return an action callable for the given name.
|
||||
#
|
||||
# @param [Symbol] name Name of the action.
|
||||
# @return [Object] A callable action sequence object, whether it
|
||||
# is a proc, object, etc.
|
||||
def action(name)
|
||||
nil
|
||||
end
|
||||
|
||||
# This method is called if the underying machine ID changes. Providers
|
||||
# can use this method to load in new data for the actual backing
|
||||
# machine or to realize that the machine is now gone (the ID can
|
||||
# become `nil`). No parameters are given, since the underlying machine
|
||||
# is simply the machine instance given to this object. And no
|
||||
# return value is necessary.
|
||||
def machine_id_changed
|
||||
end
|
||||
|
||||
# This should return a hash of information that explains how to
|
||||
# SSH into the machine. If the machine is not at a point where
|
||||
# SSH is even possible, then `nil` should be returned.
|
||||
#
|
||||
# The general structure of this returned hash should be the
|
||||
# following:
|
||||
#
|
||||
# {
|
||||
# :host => "1.2.3.4",
|
||||
# :port => "22",
|
||||
# :username => "mitchellh",
|
||||
# :private_key_path => "/path/to/my/key"
|
||||
# }
|
||||
#
|
||||
# **Note:** Vagrant only supports private key based authentication,
|
||||
# mainly for the reason that there is no easy way to exec into an
|
||||
# `ssh` prompt with a password, whereas we can pass a private key
|
||||
# via commandline.
|
||||
#
|
||||
# @return [Hash] SSH information. For the structure of this hash
|
||||
# read the accompanying documentation for this method.
|
||||
def ssh_info
|
||||
nil
|
||||
end
|
||||
|
||||
# This should return the state of the machine within this provider.
|
||||
# The state can be any symbol.
|
||||
#
|
||||
# @return [Symbol]
|
||||
def state
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,128 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/util/file_mode'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/safe_exec'
|
||||
|
||||
module Vagrant
|
||||
# Manages SSH connection information as well as allows opening an
|
||||
# SSH connection.
|
||||
class SSH
|
||||
include Util::SafeExec
|
||||
|
||||
def initialize(vm)
|
||||
@vm = vm
|
||||
@logger = Log4r::Logger.new("vagrant::ssh")
|
||||
end
|
||||
|
||||
# Returns a hash of information necessary for accessing this
|
||||
# virtual machine via SSH.
|
||||
#
|
||||
# @return [Hash]
|
||||
def info
|
||||
results = {
|
||||
:host => @vm.config.ssh.host,
|
||||
:port => @vm.config.ssh.port || @vm.driver.ssh_port(@vm.config.ssh.guest_port),
|
||||
:username => @vm.config.ssh.username,
|
||||
:forward_agent => @vm.config.ssh.forward_agent,
|
||||
:forward_x11 => @vm.config.ssh.forward_x11
|
||||
}
|
||||
|
||||
# This can happen if no port is set and for some reason Vagrant
|
||||
# can't detect an SSH port.
|
||||
raise Errors::SSHPortNotDetected if !results[:port]
|
||||
|
||||
# Determine the private key path, which is either set by the
|
||||
# configuration or uses just the built-in insecure key.
|
||||
pk_path = @vm.config.ssh.private_key_path || @vm.env.default_private_key_path
|
||||
results[:private_key_path] = File.expand_path(pk_path, @vm.env.root_path)
|
||||
|
||||
# We need to check and fix the private key permissions
|
||||
# to make sure that SSH gets a key with 0600 perms.
|
||||
check_key_permissions(results[:private_key_path])
|
||||
|
||||
# Return the results
|
||||
return results
|
||||
end
|
||||
|
||||
# Connects to the environment's virtual machine, replacing the ruby
|
||||
# process with an SSH process.
|
||||
#
|
||||
# @param [Hash] opts Options hash
|
||||
# @options opts [Boolean] :plain_mode If True, doesn't authenticate with
|
||||
# the machine, only connects, allowing the user to connect.
|
||||
def exec(opts={})
|
||||
# Get the SSH information and cache it here
|
||||
ssh_info = info
|
||||
|
||||
if Util::Platform.windows?
|
||||
raise Errors::SSHUnavailableWindows, :host => ssh_info[:host],
|
||||
:port => ssh_info[:port],
|
||||
:username => ssh_info[:username],
|
||||
:key_path => ssh_info[:private_key_path]
|
||||
end
|
||||
|
||||
raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
|
||||
|
||||
# If plain mode is enabled then we don't do any authentication (we don't
|
||||
# set a user or an identity file)
|
||||
plain_mode = opts[:plain_mode]
|
||||
|
||||
options = {}
|
||||
options[:host] = ssh_info[:host]
|
||||
options[:port] = ssh_info[:port]
|
||||
options[:username] = ssh_info[:username]
|
||||
options[:private_key_path] = ssh_info[:private_key_path]
|
||||
|
||||
# Command line options
|
||||
command_options = ["-p", options[:port].to_s, "-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "StrictHostKeyChecking=no", "-o", "LogLevel=FATAL"]
|
||||
|
||||
# Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the IdentitiesOnly option
|
||||
# (Also don't use it in plain mode, it'll skip user agents.)
|
||||
command_options += ["-o", "IdentitiesOnly=yes"] if !(Util::Platform.solaris? || plain_mode)
|
||||
|
||||
command_options += ["-i", options[:private_key_path]] if !plain_mode
|
||||
command_options += ["-o", "ForwardAgent=yes"] if ssh_info[:forward_agent]
|
||||
|
||||
# If there are extra options, then we append those
|
||||
command_options.concat(opts[:extra_args]) if opts[:extra_args]
|
||||
|
||||
if ssh_info[:forward_x11]
|
||||
# Both are required so that no warnings are shown regarding X11
|
||||
command_options += ["-o", "ForwardX11=yes"]
|
||||
command_options += ["-o", "ForwardX11Trusted=yes"]
|
||||
end
|
||||
|
||||
host_string = options[:host]
|
||||
host_string = "#{options[:username]}@#{host_string}" if !plain_mode
|
||||
command_options << host_string
|
||||
@logger.info("Invoking SSH: #{command_options.inspect}")
|
||||
safe_exec("ssh", *command_options)
|
||||
end
|
||||
|
||||
# Checks the file permissions for a private key, resetting them
|
||||
# if needed.
|
||||
def check_key_permissions(key_path)
|
||||
# Windows systems don't have this issue
|
||||
return if Util::Platform.windows?
|
||||
|
||||
@logger.debug("Checking key permissions: #{key_path}")
|
||||
stat = File.stat(key_path)
|
||||
|
||||
if stat.owned? && Util::FileMode.from_octal(stat.mode) != "600"
|
||||
@logger.info("Attempting to correct key permissions to 0600")
|
||||
File.chmod(0600, key_path)
|
||||
|
||||
stat = File.stat(key_path)
|
||||
if Util::FileMode.from_octal(stat.mode) != "600"
|
||||
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
||||
end
|
||||
end
|
||||
rescue Errno::EPERM
|
||||
# This shouldn't happen since we verified we own the file, but
|
||||
# it is possible in theory, so we raise an error.
|
||||
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,8 +6,8 @@ module Vagrant
|
|||
# This issue causes `exec` to fail if there is more than one system
|
||||
# thread. In that case, `safe_exec` automatically falls back to
|
||||
# forking.
|
||||
module SafeExec
|
||||
def safe_exec(command, *args)
|
||||
class SafeExec
|
||||
def self.exec(command, *args)
|
||||
# Create a list of things to rescue from. Since this is OS
|
||||
# specific, we need to do some defined? checks here to make
|
||||
# sure they exist.
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
require "log4r"
|
||||
|
||||
require "vagrant/util/file_mode"
|
||||
require "vagrant/util/platform"
|
||||
require "vagrant/util/safe_exec"
|
||||
|
||||
module Vagrant
|
||||
module Util
|
||||
# This is a class that has helpers on it for dealing with SSH. These
|
||||
# helpers don't depend on any part of Vagrant except what is given
|
||||
# via the parameters.
|
||||
class SSH
|
||||
LOGGER = Log4r::Logger.new("vagrant::util::ssh")
|
||||
|
||||
# Checks that the permissions for a private key are valid, and fixes
|
||||
# them if possible. SSH requires that permissions on the private key
|
||||
# are 0600 on POSIX based systems. This will make a best effort to
|
||||
# fix these permissions if they are not properly set.
|
||||
#
|
||||
# @param [Pathname] key_path The path to the private key.
|
||||
def self.check_key_permissions(key_path)
|
||||
# Don't do anything if we're on Windows, since Windows doesn't worry
|
||||
# about key permissions.
|
||||
return if Platform.windows?
|
||||
|
||||
LOGGER.debug("Checking key permissions: #{key_path}")
|
||||
stat = key_path.stat
|
||||
|
||||
if stat.owned? && FileMode.from_octal(stat.mode) != "600"
|
||||
LOGGER.info("Attempting to correct key permissions to 0600")
|
||||
key_path.chmod(0600)
|
||||
|
||||
# Re-stat the file to get the new mode, and verify it worked
|
||||
stat = key_path.stat
|
||||
if FileMode.from_octal(stat.mode) != "600"
|
||||
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
||||
end
|
||||
end
|
||||
rescue Errno::EPERM
|
||||
# This shouldn't happen since we verify we own the file, but
|
||||
# it is possible in theory, so we raise an error.
|
||||
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
||||
end
|
||||
|
||||
# Halts the running of this process and replaces it with a full-fledged
|
||||
# SSH shell into a remote machine.
|
||||
#
|
||||
# Note: This method NEVER returns. The process ends after this.
|
||||
#
|
||||
# @param [Hash] ssh_info This is the SSH information. For the keys
|
||||
# required please see the documentation of {Machine#ssh_info}.
|
||||
# @param [Hash] opts These are additional options that are supported
|
||||
# by exec.
|
||||
def self.exec(ssh_info, opts={})
|
||||
# If we're running Windows, raise an exception since we currently
|
||||
# still don't support exec-ing into SSH. In the future this should
|
||||
# certainly be possible if we can detect we're in an environment that
|
||||
# supports it.
|
||||
if Platform.windows?
|
||||
raise Errors::SSHUnavailableWindows,
|
||||
:host => ssh_info[:host],
|
||||
:port => ssh_info[:port],
|
||||
:username => ssh_info[:username],
|
||||
:key_path => ssh_info[:private_key_path]
|
||||
end
|
||||
|
||||
# Verify that we have SSH available on the system.
|
||||
raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
|
||||
|
||||
# If plain mode is enabled then we don't do any authentication (we don't
|
||||
# set a user or an identity file)
|
||||
plain_mode = opts[:plain_mode]
|
||||
|
||||
options = {}
|
||||
options[:host] = ssh_info[:host]
|
||||
options[:port] = ssh_info[:port]
|
||||
options[:username] = ssh_info[:username]
|
||||
options[:private_key_path] = ssh_info[:private_key_path]
|
||||
|
||||
# Command line options
|
||||
command_options = [
|
||||
"-p", options[:port].to_s,
|
||||
"-o", "LogLevel=FATAL",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null"]
|
||||
|
||||
# Configurables
|
||||
command_options += ["-o", "ForwardAgent=yes"] if ssh_info[:forward_agent]
|
||||
command_options.concat(opts[:extra_args]) if opts[:extra_args]
|
||||
|
||||
# Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the
|
||||
# IdentitiesOnly option. Also, we don't enable it in plain mode so
|
||||
# that SSH properly searches our identities and tries to do it itself.
|
||||
if !Platform.solaris? && !plain_mode
|
||||
command_options += ["-o", "IdentitiesOnly=yes"]
|
||||
end
|
||||
|
||||
# If we're not in plain mode, attach the private key path.
|
||||
command_options += ["-i", options[:private_key_path].to_s] if !plain_mode
|
||||
|
||||
if ssh_info[:forward_x11]
|
||||
# Both are required so that no warnings are shown regarding X11
|
||||
command_options += [
|
||||
"-o", "ForwardX11=yes",
|
||||
"-o", "ForwardX11Trusted=yes"]
|
||||
end
|
||||
|
||||
# Build up the host string for connecting
|
||||
host_string = options[:host]
|
||||
host_string = "#{options[:username]}@#{host_string}" if !plain_mode
|
||||
command_options << host_string
|
||||
|
||||
# Invoke SSH with all our options
|
||||
LOGGER.info("Invoking SSH: #{command_options.inspect}")
|
||||
SafeExec.exec("ssh", *command_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,205 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
module Vagrant
|
||||
class VM
|
||||
include Vagrant::Util
|
||||
|
||||
attr_reader :uuid
|
||||
attr_reader :env
|
||||
attr_reader :name
|
||||
attr_reader :vm
|
||||
attr_reader :box
|
||||
attr_reader :config
|
||||
attr_reader :driver
|
||||
|
||||
def initialize(name, env, config, opts=nil)
|
||||
@logger = Log4r::Logger.new("vagrant::vm")
|
||||
|
||||
@name = name
|
||||
@vm = nil
|
||||
@env = env
|
||||
@config = config
|
||||
@box = nil
|
||||
@box = env.boxes.find(config.vm.box, :virtualbox) if config.vm.box
|
||||
|
||||
opts ||= {}
|
||||
if opts[:base]
|
||||
# The name is the ID we use.
|
||||
@uuid = name
|
||||
else
|
||||
# Load the UUID if its saved.
|
||||
active = env.local_data[:active] || {}
|
||||
@uuid = active[@name.to_s]
|
||||
end
|
||||
|
||||
# Reload ourselves to get the state
|
||||
reload!
|
||||
|
||||
# Load the associated guest.
|
||||
load_guest!
|
||||
@loaded_guest_distro = false
|
||||
end
|
||||
|
||||
# Loads the guest associated with the VM. The guest class is
|
||||
# responsible for OS-specific functionality. More information
|
||||
# can be found by reading the documentation on {Vagrant::Guest::Base}.
|
||||
#
|
||||
# **This method should never be called manually.**
|
||||
def load_guest!(guest=nil)
|
||||
guest ||= config.vm.guest
|
||||
@logger.info("Loading guest: #{guest}")
|
||||
|
||||
if guest.is_a?(Class)
|
||||
raise Errors::VMGuestError, :_key => :invalid_class, :guest => guest.to_s if !(guest <= Vagrant.plugin("1", :guest))
|
||||
@guest = guest.new(self)
|
||||
elsif guest.is_a?(Symbol)
|
||||
# Look for the guest as a registered plugin
|
||||
guest_klass = nil
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
if plugin.guest.has_key?(guest)
|
||||
guest_klass = plugin.guest[guest]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
raise Errors::VMGuestError, :_key => :unknown_type, :guest => guest.to_s if !guest_klass
|
||||
@guest = guest_klass.new(self)
|
||||
else
|
||||
raise Errors::VMGuestError, :unspecified
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a channel object to communicate with the virtual
|
||||
# machine.
|
||||
def channel
|
||||
@channel ||= Communication::SSH.new(self)
|
||||
end
|
||||
|
||||
# Returns the guest for this VM, loading the distro of the system if
|
||||
# we can.
|
||||
def guest
|
||||
if !@loaded_guest_distro && state == :running
|
||||
# Load the guest distro for the first time
|
||||
result = @guest.distro_dispatch
|
||||
load_guest!(result)
|
||||
@loaded_guest_distro = true
|
||||
@logger.info("Guest class: #{@guest.class}")
|
||||
end
|
||||
|
||||
@guest
|
||||
end
|
||||
|
||||
# Access the {Vagrant::SSH} object associated with this VM, which
|
||||
# is used to get SSH credentials with the virtual machine.
|
||||
def ssh
|
||||
@ssh ||= SSH.new(self)
|
||||
end
|
||||
|
||||
# Returns the state of the VM as a symbol.
|
||||
#
|
||||
# @return [Symbol]
|
||||
def state
|
||||
return :not_created if !@uuid
|
||||
state = @driver.read_state
|
||||
return :not_created if !state
|
||||
return state
|
||||
end
|
||||
|
||||
# Returns a boolean true if the VM has been created, otherwise
|
||||
# returns false.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def created?
|
||||
state != :not_created
|
||||
end
|
||||
|
||||
# Sets the currently active VM for this VM. If the VM is a valid,
|
||||
# created virtual machine, then it will also update the local data
|
||||
# to persist the VM. Otherwise, it will remove itself from the
|
||||
# local data (if it exists).
|
||||
def uuid=(value)
|
||||
env.local_data[:active] ||= {}
|
||||
if value
|
||||
env.local_data[:active][name.to_s] = value
|
||||
else
|
||||
env.local_data[:active].delete(name.to_s)
|
||||
end
|
||||
|
||||
# Commit the local data so that the next time vagrant is initialized,
|
||||
# it realizes the VM exists
|
||||
env.local_data.commit
|
||||
|
||||
# Store the uuid and reload the instance
|
||||
@uuid = value
|
||||
reload!
|
||||
end
|
||||
|
||||
def reload!
|
||||
begin
|
||||
@driver = Driver::VirtualBox.new(@uuid)
|
||||
rescue Driver::VirtualBox::VMNotFound
|
||||
# Clear the UUID since this VM doesn't exist.
|
||||
@uuid = nil
|
||||
|
||||
# Reset the driver. This shouldn't raise a VMNotFound since we won't
|
||||
# feed it a UUID.
|
||||
@driver = Driver::VirtualBox.new
|
||||
end
|
||||
end
|
||||
|
||||
def package(options=nil)
|
||||
run_action(:package, { "validate" => false }.merge(options || {}))
|
||||
end
|
||||
|
||||
def up(options=nil)
|
||||
run_action(:up, options)
|
||||
end
|
||||
|
||||
def start(options=nil)
|
||||
return if state == :running
|
||||
return resume if state == :saved
|
||||
|
||||
run_action(:start, options)
|
||||
end
|
||||
|
||||
def halt(options=nil)
|
||||
run_action(:halt, options)
|
||||
end
|
||||
|
||||
def reload(options=nil)
|
||||
run_action(:reload, options)
|
||||
end
|
||||
|
||||
def provision
|
||||
run_action(:provision)
|
||||
end
|
||||
|
||||
def destroy
|
||||
run_action(:destroy)
|
||||
end
|
||||
|
||||
def suspend
|
||||
run_action(:suspend)
|
||||
end
|
||||
|
||||
def resume
|
||||
run_action(:resume)
|
||||
end
|
||||
|
||||
def ui
|
||||
return @_ui if defined?(@_ui)
|
||||
@_ui = @env.ui.dup
|
||||
@_ui.resource = @name
|
||||
@_ui
|
||||
end
|
||||
|
||||
def run_action(name, options=nil)
|
||||
options = {
|
||||
:vm => self,
|
||||
:ui => ui
|
||||
}.merge(options || {})
|
||||
|
||||
env.action_runner.run(name, options)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,11 +7,11 @@ module VagrantPlugins
|
|||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant box add <name> <url>"
|
||||
opts.separator ""
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box add <name> <url>"
|
||||
o.separator ""
|
||||
|
||||
opts.on("-f", "--force", "Overwrite an existing box if it exists.") do |f|
|
||||
o.on("-f", "--force", "Overwrite an existing box if it exists.") do |f|
|
||||
options[:force] = f
|
||||
end
|
||||
end
|
||||
|
@ -28,8 +28,7 @@ module VagrantPlugins
|
|||
existing.destroy! if existing
|
||||
end
|
||||
|
||||
# Invoke the "box_add" middleware sequence.
|
||||
@env.action_runner.run(:box_add, {
|
||||
@env.action_runner.run(Vagrant::Action.action_box_add, {
|
||||
:box_name => argv[0],
|
||||
:box_provider => :virtualbox,
|
||||
:box_url => argv[1]
|
||||
|
|
|
@ -4,11 +4,11 @@ module VagrantPlugins
|
|||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant destroy [vm-name]"
|
||||
opts.separator ""
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant destroy [vm-name]"
|
||||
o.separator ""
|
||||
|
||||
opts.on("-f", "--force", "Destroy without confirmation.") do |f|
|
||||
o.on("-f", "--force", "Destroy without confirmation.") do |f|
|
||||
options[:force] = f
|
||||
end
|
||||
end
|
||||
|
@ -19,45 +19,7 @@ module VagrantPlugins
|
|||
|
||||
@logger.debug("'Destroy' each target VM...")
|
||||
with_target_vms(argv, :reverse => true) do |vm|
|
||||
if vm.created?
|
||||
# Boolean whether we should actually go through with the destroy
|
||||
# or not. This is true only if the "--force" flag is set or if the
|
||||
# user confirms it.
|
||||
do_destroy = false
|
||||
|
||||
if options[:force]
|
||||
do_destroy = true
|
||||
else
|
||||
choice = nil
|
||||
|
||||
begin
|
||||
choice = @env.ui.ask(I18n.t("vagrant.commands.destroy.confirmation",
|
||||
:name => vm.name))
|
||||
rescue Interrupt
|
||||
# Ctrl-C was pressed (or SIGINT). We just exit immediately
|
||||
# with a non-zero exit status.
|
||||
return 1
|
||||
rescue Vagrant::Errors::UIExpectsTTY
|
||||
# We raise a more specific error but one which basically
|
||||
# means the same thing.
|
||||
raise Vagrant::Errors::DestroyRequiresForce
|
||||
end
|
||||
|
||||
do_destroy = choice.upcase == "Y"
|
||||
end
|
||||
|
||||
if do_destroy
|
||||
@logger.info("Destroying: #{vm.name}")
|
||||
vm.destroy
|
||||
else
|
||||
@logger.info("Not destroying #{vm.name} since confirmation was declined.")
|
||||
@env.ui.success(I18n.t("vagrant.commands.destroy.will_not_destroy",
|
||||
:name => vm.name), :prefix => false)
|
||||
end
|
||||
else
|
||||
@logger.info("Not destroying #{vm.name}, since it isn't created.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
vm.action(:destroy)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -22,13 +22,8 @@ module VagrantPlugins
|
|||
|
||||
@logger.debug("Halt command: #{argv.inspect} #{options.inspect}")
|
||||
with_target_vms(argv) do |vm|
|
||||
if vm.created?
|
||||
@logger.info("Halting #{vm.name}")
|
||||
vm.halt(:force => options[:force])
|
||||
else
|
||||
@logger.info("Not halting #{vm.name}, since not created.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
# XXX: "force"
|
||||
vm.action(:halt)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -6,25 +6,25 @@ module VagrantPlugins
|
|||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant package [vm-name] [--base name] [--output name.box]"
|
||||
opts.separator " [--include one,two,three] [--vagrantfile file]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant package [vm-name] [--base name] [--output name.box]"
|
||||
o.separator " [--include one,two,three] [--vagrantfile file]"
|
||||
|
||||
opts.separator ""
|
||||
o.separator ""
|
||||
|
||||
opts.on("--base NAME", "Name of a VM in virtualbox to package as a base box") do |b|
|
||||
o.on("--base NAME", "Name of a VM in virtualbox to package as a base box") do |b|
|
||||
options[:base] = b
|
||||
end
|
||||
|
||||
opts.on("--output NAME", "Name of the file to output") do |o|
|
||||
options[:output] = o
|
||||
o.on("--output NAME", "Name of the file to output") do |output|
|
||||
options[:output] = output
|
||||
end
|
||||
|
||||
opts.on("--include x,y,z", Array, "Additional files to package with the box.") do |i|
|
||||
o.on("--include x,y,z", Array, "Additional files to package with the box.") do |i|
|
||||
options[:include] = i
|
||||
end
|
||||
|
||||
opts.on("--vagrantfile file", "Vagrantfile to package with the box.") do |v|
|
||||
o.on("--vagrantfile file", "Vagrantfile to package with the box.") do |v|
|
||||
options[:vagrantfile] = v
|
||||
end
|
||||
end
|
||||
|
@ -47,15 +47,23 @@ module VagrantPlugins
|
|||
protected
|
||||
|
||||
def package_base(options)
|
||||
vm = Vagrant::VM.new(options[:base], @env, @env.config.global, :base => true)
|
||||
raise Vagrant::Errors::BaseVMNotFound, :name => options[:base] if !vm.created?
|
||||
# XXX: This whole thing is hardcoded and very temporary. The whole
|
||||
# `vagrant package --base` process is deprecated for something much
|
||||
# better in the future. We just hardcode this to keep VirtualBox working
|
||||
# for now.
|
||||
provider = nil
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
provider = plugin.provider.get(:virtualbox)
|
||||
break if provider
|
||||
end
|
||||
|
||||
vm = Vagrant::Machine.new(options[:base], provider, @env.config.global, nil, @env, true)
|
||||
@logger.debug("Packaging base VM: #{vm.name}")
|
||||
package_vm(vm, options)
|
||||
end
|
||||
|
||||
def package_target(name, options)
|
||||
with_target_vms(name, :single_target => true) do |vm|
|
||||
raise Vagrant::Errors::VMNotCreatedError if !vm.created?
|
||||
@logger.debug("Packaging VM: #{vm.name}")
|
||||
package_vm(vm, options)
|
||||
end
|
||||
|
@ -68,7 +76,7 @@ module VagrantPlugins
|
|||
acc
|
||||
end
|
||||
|
||||
vm.package(opts)
|
||||
vm.action(:package, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,10 +4,8 @@ module VagrantPlugins
|
|||
module CommandProvision
|
||||
class Command < Vagrant.plugin("1", :command)
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant provision [vm-name]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant provision [vm-name]"
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -16,20 +14,8 @@ module VagrantPlugins
|
|||
|
||||
# Go over each VM and provision!
|
||||
@logger.debug("'provision' each target VM...")
|
||||
with_target_vms(argv) do |vm|
|
||||
|
||||
if vm.created?
|
||||
if vm.state == :running
|
||||
@logger.info("Provisioning: #{vm.name}")
|
||||
vm.provision
|
||||
else
|
||||
@logger.info("#{vm.name} not running. Not provisioning.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_running")
|
||||
end
|
||||
else
|
||||
@logger.info("#{vm.name} not created. Not provisioning.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
with_target_vms(argv) do |machine|
|
||||
machine.action(:provision)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -14,10 +14,10 @@ module VagrantPlugins
|
|||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant reload [vm-name]"
|
||||
opts.separator ""
|
||||
build_start_options(opts, options)
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant reload [vm-name]"
|
||||
o.separator ""
|
||||
build_start_options(o, options)
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -25,14 +25,8 @@ module VagrantPlugins
|
|||
return if !argv
|
||||
|
||||
@logger.debug("'reload' each target VM...")
|
||||
with_target_vms(argv) do |vm|
|
||||
if vm.created?
|
||||
@logger.info("Reloading: #{vm.name}")
|
||||
vm.reload(options)
|
||||
else
|
||||
@logger.info("Not created: #{vm.name}. Not reloading.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
with_target_vms(argv) do |machine|
|
||||
machine.action(:reload)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -4,10 +4,8 @@ module VagrantPlugins
|
|||
module CommandResume
|
||||
class Command < Vagrant.plugin("1", :command)
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant resume [vm-name]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant resume [vm-name]"
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -15,14 +13,8 @@ module VagrantPlugins
|
|||
return if !argv
|
||||
|
||||
@logger.debug("'resume' each target VM...")
|
||||
with_target_vms(argv) do |vm|
|
||||
if vm.created?
|
||||
@logger.info("Resume: #{vm.name}")
|
||||
vm.resume
|
||||
else
|
||||
@logger.info("Not created: #{vm.name}. Not resuming.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
with_target_vms(argv) do |machine|
|
||||
machine.action(:resume)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -37,53 +37,29 @@ module VagrantPlugins
|
|||
|
||||
# Execute the actual SSH
|
||||
with_target_vms(argv, :single_target => true) do |vm|
|
||||
# Basic checks that are required for proper SSH
|
||||
raise Vagrant::Errors::VMNotCreatedError if !vm.created?
|
||||
raise Vagrant::Errors::VMInaccessible if !vm.state == :inaccessible
|
||||
raise Vagrant::Errors::VMNotRunningError if vm.state != :running
|
||||
|
||||
if options[:command]
|
||||
ssh_execute(vm, options[:command])
|
||||
@logger.debug("Executing single command on remote machine: #{options[:command]}")
|
||||
env = vm.action(:ssh_run, :ssh_run_command => options[:command])
|
||||
|
||||
# Exit with the exit status of the command or a 0 if we didn't
|
||||
# get one.
|
||||
exit_status = env[:ssh_run_exit_status] || 0
|
||||
return exit_status
|
||||
else
|
||||
opts = {
|
||||
:plain_mode => options[:plain_mode],
|
||||
:extra_args => options[:ssh_args]
|
||||
}
|
||||
|
||||
ssh_connect(vm, opts)
|
||||
end
|
||||
end
|
||||
@logger.debug("Invoking `ssh` action on machine")
|
||||
vm.action(:ssh, :ssh_opts => opts)
|
||||
|
||||
# Success, exit status 0
|
||||
0
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def ssh_execute(vm, command=nil)
|
||||
exit_status = 0
|
||||
|
||||
@logger.debug("Executing command: #{command}")
|
||||
exit_status = vm.channel.execute(command, :error_check => false) do |type, data|
|
||||
# Determine the proper channel to send the output onto depending
|
||||
# on the type of data we are receiving.
|
||||
channel = type == :stdout ? :out : :error
|
||||
|
||||
# Print the SSH output as it comes in, but don't prefix it and don't
|
||||
# force a new line so that the output is properly preserved
|
||||
vm.ui.info(data.to_s,
|
||||
:prefix => false,
|
||||
:new_line => false,
|
||||
:channel => channel)
|
||||
end
|
||||
|
||||
# Exit with the exit status we got from executing the command
|
||||
exit exit_status
|
||||
end
|
||||
|
||||
def ssh_connect(vm, opts)
|
||||
@logger.debug("`exec` into ssh prompt")
|
||||
vm.ssh.exec(opts)
|
||||
# We should never reach this point, since the point of `ssh`
|
||||
# is to exec into the proper SSH shell, but we'll just return
|
||||
# an exit status of 0 anyways.
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,12 +10,11 @@ module VagrantPlugins
|
|||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant ssh-config [vm-name] [--host name]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant ssh-config [vm-name] [--host name]"
|
||||
o.separator ""
|
||||
|
||||
opts.separator ""
|
||||
|
||||
opts.on("--host NAME", "Name the host for the config..") do |h|
|
||||
o.on("--host COMMAND", "Name the host for the config..") do |h|
|
||||
options[:host] = h
|
||||
end
|
||||
end
|
||||
|
@ -23,13 +22,12 @@ module VagrantPlugins
|
|||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
with_target_vms(argv, :single_target => true) do |vm|
|
||||
raise Vagrant::Errors::VMNotCreatedError if !vm.created?
|
||||
raise Vagrant::Errors::VMInaccessible if !vm.state == :inaccessible
|
||||
with_target_vms(argv, :single_target => true) do |machine|
|
||||
ssh_info = machine.ssh_info
|
||||
raise Vagrant::Errors::SSHNotReady if ssh_info.nil?
|
||||
|
||||
ssh_info = vm.ssh.info
|
||||
variables = {
|
||||
:host_key => options[:host] || vm.name || "vagrant",
|
||||
:host_key => options[:host] || machine.name || "vagrant",
|
||||
:ssh_host => ssh_info[:host],
|
||||
:ssh_port => ssh_info[:port],
|
||||
:ssh_user => ssh_info[:username],
|
||||
|
|
|
@ -4,10 +4,8 @@ module VagrantPlugins
|
|||
module CommandSuspend
|
||||
class Command < Vagrant.plugin("1", :command)
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant suspend [vm-name]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant suspend [vm-name]"
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -16,13 +14,7 @@ module VagrantPlugins
|
|||
|
||||
@logger.debug("'suspend' each target VM...")
|
||||
with_target_vms(argv) do |vm|
|
||||
if vm.created?
|
||||
@logger.info("Suspending: #{vm.name}")
|
||||
vm.suspend
|
||||
else
|
||||
@logger.info("Not created: #{vm.name}. Not suspending.")
|
||||
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
end
|
||||
vm.action(:suspend)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -11,10 +11,10 @@ module VagrantPlugins
|
|||
|
||||
def execute
|
||||
options = {}
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: vagrant up [vm-name] [--[no-]provision] [-h]"
|
||||
opts.separator ""
|
||||
build_start_options(opts, options)
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant up [vm-name] [--[no-]provision] [-h]"
|
||||
o.separator ""
|
||||
build_start_options(o, options)
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
|
@ -23,15 +23,8 @@ module VagrantPlugins
|
|||
|
||||
# Go over each VM and bring it up
|
||||
@logger.debug("'Up' each target VM...")
|
||||
with_target_vms(argv) do |vm|
|
||||
if vm.created?
|
||||
@logger.info("Booting: #{vm.name}")
|
||||
vm.ui.info I18n.t("vagrant.commands.up.vm_created")
|
||||
vm.start(options)
|
||||
else
|
||||
@logger.info("Creating: #{vm.name}")
|
||||
vm.up(options)
|
||||
end
|
||||
with_target_vms(argv) do |machine|
|
||||
machine.action(:up)
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -8,16 +8,22 @@ require 'vagrant/util/ansi_escape_code_remover'
|
|||
require 'vagrant/util/file_mode'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/retryable'
|
||||
require 'vagrant/util/ssh'
|
||||
|
||||
module Vagrant
|
||||
module Communication
|
||||
# Provides communication with the VM via SSH.
|
||||
class SSH < Base
|
||||
include Util::ANSIEscapeCodeRemover
|
||||
include Util::Retryable
|
||||
module VagrantPlugins
|
||||
module CommunicatorSSH
|
||||
# This class provides communication with the VM via SSH.
|
||||
class Communicator < Vagrant.plugin("1", :communicator)
|
||||
include Vagrant::Util::ANSIEscapeCodeRemover
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def initialize(vm)
|
||||
@vm = vm
|
||||
def self.match?(machine)
|
||||
# All machines are currently expected to have SSH.
|
||||
true
|
||||
end
|
||||
|
||||
def initialize(machine)
|
||||
@machine = machine
|
||||
@logger = Log4r::Logger.new("vagrant::communication::ssh")
|
||||
@connection = nil
|
||||
end
|
||||
|
@ -31,7 +37,7 @@ module Vagrant
|
|||
# If we reached this point then we successfully connected
|
||||
@logger.info("SSH is ready!")
|
||||
true
|
||||
rescue Errors::VagrantError => e
|
||||
rescue Vagrant::Errors::VagrantError => e
|
||||
# We catch a `VagrantError` which would signal that something went
|
||||
# wrong expectedly in the `connect`, which means we didn't connect.
|
||||
@logger.info("SSH not up: #{e.inspect}")
|
||||
|
@ -41,7 +47,7 @@ module Vagrant
|
|||
def execute(command, opts=nil, &block)
|
||||
opts = {
|
||||
:error_check => true,
|
||||
:error_class => Errors::VagrantError,
|
||||
:error_class => Vagrant::Errors::VagrantError,
|
||||
:error_key => :ssh_bad_exit_status,
|
||||
:command => command,
|
||||
:sudo => false
|
||||
|
@ -79,6 +85,11 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
def test(command, opts=nil)
|
||||
opts = { :error_check => false }.merge(opts || {})
|
||||
execute(command, opts) == 0
|
||||
end
|
||||
|
||||
def upload(from, to)
|
||||
@logger.debug("Uploading: #{from} to #{to}")
|
||||
|
||||
|
@ -93,7 +104,7 @@ module Vagrant
|
|||
|
||||
# Otherwise, it is a permission denied, so let's raise a proper
|
||||
# exception
|
||||
raise Errors::SCPPermissionDenied, :path => from.to_s
|
||||
raise Vagrant::Errors::SCPPermissionDenied, :path => from.to_s
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -120,7 +131,8 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
ssh_info = @vm.ssh.info
|
||||
# XXX: We need to raise some exception if SSH is not ready
|
||||
ssh_info = @machine.ssh_info
|
||||
|
||||
# Build the options we'll use to initiate the connection via Net::SSH
|
||||
opts = {
|
||||
|
@ -134,38 +146,38 @@ module Vagrant
|
|||
}
|
||||
|
||||
# Check that the private key permissions are valid
|
||||
@vm.ssh.check_key_permissions(ssh_info[:private_key_path])
|
||||
Vagrant::Util::SSH.check_key_permissions(ssh_info[:private_key_path])
|
||||
|
||||
# Connect to SSH, giving it a few tries
|
||||
connection = nil
|
||||
begin
|
||||
exceptions = [Errno::ECONNREFUSED, Net::SSH::Disconnect, Timeout::Error]
|
||||
connection = retryable(:tries => @vm.config.ssh.max_tries, :on => exceptions) do
|
||||
Timeout.timeout(@vm.config.ssh.timeout) do
|
||||
connection = retryable(:tries => @machine.config.ssh.max_tries, :on => exceptions) do
|
||||
Timeout.timeout(@machine.config.ssh.timeout) do
|
||||
@logger.info("Attempting to connect to SSH: #{ssh_info[:host]}:#{ssh_info[:port]}")
|
||||
Net::SSH.start(ssh_info[:host], ssh_info[:username], opts)
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
# This happens if we continued to timeout when attempting to connect.
|
||||
raise Errors::SSHConnectionTimeout
|
||||
raise Vagrant::Errors::SSHConnectionTimeout
|
||||
rescue Net::SSH::AuthenticationFailed
|
||||
# This happens if authentication failed. We wrap the error in our
|
||||
# own exception.
|
||||
raise Errors::SSHAuthenticationFailed
|
||||
raise Vagrant::Errors::SSHAuthenticationFailed
|
||||
rescue Net::SSH::Disconnect
|
||||
# This happens if the remote server unexpectedly closes the
|
||||
# connection. This is usually raised when SSH is running on the
|
||||
# other side but can't properly setup a connection. This is
|
||||
# usually a server-side issue.
|
||||
raise Errors::SSHDisconnected
|
||||
raise Vagrant::Errors::SSHDisconnected
|
||||
rescue Errno::ECONNREFUSED
|
||||
# This is raised if we failed to connect the max amount of times
|
||||
raise Errors::SSHConnectionRefused
|
||||
raise Vagrant::Errors::SSHConnectionRefused
|
||||
rescue NotImplementedError
|
||||
# This is raised if a private key type that Net-SSH doesn't support
|
||||
# is used. Show a nicer error.
|
||||
raise Errors::SSHKeyTypeNotSupported
|
||||
raise Vagrant::Errors::SSHKeyTypeNotSupported
|
||||
end
|
||||
|
||||
@connection = connection
|
||||
|
@ -187,7 +199,7 @@ module Vagrant
|
|||
|
||||
# Determine the shell to execute. If we are using `sudo` then we
|
||||
# need to wrap the shell in a `sudo` call.
|
||||
shell = @vm.config.ssh.shell
|
||||
shell = @machine.config.ssh.shell
|
||||
shell = "sudo -H #{shell}" if sudo
|
||||
|
||||
# Open the channel so we can execute or command
|
||||
|
@ -248,7 +260,7 @@ module Vagrant
|
|||
end
|
||||
rescue Net::SCP::Error => e
|
||||
# If we get the exit code of 127, then this means SCP is unavailable.
|
||||
raise Errors::SCPUnavailable if e.message =~ /\(127\)/
|
||||
raise Vagrant::Errors::SCPUnavailable if e.message =~ /\(127\)/
|
||||
|
||||
# Otherwise, just raise the error up
|
||||
raise
|
|
@ -0,0 +1,19 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommunicatorSSH
|
||||
class Plugin < Vagrant.plugin("1")
|
||||
name "ssh communiator"
|
||||
description <<-DESC
|
||||
This plugin allows Vagrant to communicate with remote machines using
|
||||
SSH as the underlying protocol, powered internally by Ruby's
|
||||
net-ssh library.
|
||||
DESC
|
||||
|
||||
communicator("ssh") do
|
||||
require File.expand_path("../communicator", __FILE__)
|
||||
Communicator
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,23 +16,25 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def distro_dispatch
|
||||
if @vm.channel.test("cat /etc/debian_version")
|
||||
return :debian if @vm.channel.test("cat /proc/version | grep 'Debian'")
|
||||
return :ubuntu if @vm.channel.test("cat /proc/version | grep 'Ubuntu'")
|
||||
@vm.communicate.tap do |comm|
|
||||
if comm.test("cat /etc/debian_version") == 0
|
||||
return :debian if comm.test("cat /proc/version | grep 'Debian'") == 0
|
||||
return :ubuntu if comm.test("cat /proc/version | grep 'Ubuntu'") == 0
|
||||
end
|
||||
|
||||
return :gentoo if @vm.channel.test("cat /etc/gentoo-release")
|
||||
return :fedora if @vm.channel.test("grep 'Fedora release 16' /etc/redhat-release")
|
||||
return :redhat if @vm.channel.test("cat /etc/redhat-release")
|
||||
return :suse if @vm.channel.test("cat /etc/SuSE-release")
|
||||
return :arch if @vm.channel.test("cat /etc/arch-release")
|
||||
return :gentoo if comm.test("cat /etc/gentoo-release") == 0
|
||||
return :fedora if comm.test("grep 'Fedora release 16' /etc/redhat-release") == 0
|
||||
return :redhat if comm.test("cat /etc/redhat-release") == 0
|
||||
return :suse if comm.test("cat /etc/SuSE-release") == 0
|
||||
return :arch if comm.test("cat /etc/arch-release") == 0
|
||||
end
|
||||
|
||||
# Can't detect the distro, assume vanilla linux
|
||||
nil
|
||||
end
|
||||
|
||||
def halt
|
||||
@vm.channel.sudo("shutdown -h now")
|
||||
@vm.communicate.sudo("shutdown -h now")
|
||||
|
||||
# Wait until the VM's state is actually powered off. If this doesn't
|
||||
# occur within a reasonable amount of time (15 seconds by default),
|
||||
|
@ -50,9 +52,9 @@ module VagrantPlugins
|
|||
real_guestpath = expanded_guest_path(guestpath)
|
||||
@logger.debug("Shell expanded guest path: #{real_guestpath}")
|
||||
|
||||
@vm.channel.sudo("mkdir -p #{real_guestpath}")
|
||||
@vm.communicate.sudo("mkdir -p #{real_guestpath}")
|
||||
mount_folder(name, real_guestpath, options)
|
||||
@vm.channel.sudo("chown `id -u #{options[:owner]}`:`id -g #{options[:group]}` #{real_guestpath}")
|
||||
@vm.communicate.sudo("chown `id -u #{options[:owner]}`:`id -g #{options[:group]}` #{real_guestpath}")
|
||||
end
|
||||
|
||||
def mount_nfs(ip, folders)
|
||||
|
@ -63,8 +65,8 @@ module VagrantPlugins
|
|||
real_guestpath = expanded_guest_path(opts[:guestpath])
|
||||
|
||||
# Do the actual creating and mounting
|
||||
@vm.channel.sudo("mkdir -p #{real_guestpath}")
|
||||
@vm.channel.sudo("mount -o vers=#{opts[:nfs_version]} #{ip}:'#{opts[:hostpath]}' #{real_guestpath}",
|
||||
@vm.communicate.sudo("mkdir -p #{real_guestpath}")
|
||||
@vm.communicate.sudo("mount -o vers=#{opts[:nfs_version]} #{ip}:'#{opts[:hostpath]}' #{real_guestpath}",
|
||||
:error_class => LinuxError,
|
||||
:error_key => :mount_nfs_fail)
|
||||
end
|
||||
|
@ -80,7 +82,7 @@ module VagrantPlugins
|
|||
# @return [String] The expanded guestpath
|
||||
def expanded_guest_path(guestpath)
|
||||
real_guestpath = nil
|
||||
@vm.channel.execute("printf #{guestpath}") do |type, data|
|
||||
@vm.communicate.execute("printf #{guestpath}") do |type, data|
|
||||
if type == :stdout
|
||||
real_guestpath ||= ""
|
||||
real_guestpath += data
|
||||
|
@ -105,7 +107,7 @@ module VagrantPlugins
|
|||
attempts = 0
|
||||
while true
|
||||
success = true
|
||||
@vm.channel.sudo("mount -t vboxsf #{mount_options} #{name} #{guestpath}") do |type, data|
|
||||
@vm.communicate.sudo("mount -t vboxsf #{mount_options} #{name} #{guestpath}") do |type, data|
|
||||
success = false if type == :stderr && data =~ /No such device/i
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
require "vagrant/action/builder"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
autoload :Boot, File.expand_path("../action/boot", __FILE__)
|
||||
autoload :CheckAccessible, File.expand_path("../action/check_accessible", __FILE__)
|
||||
autoload :CheckBox, File.expand_path("../action/check_box", __FILE__)
|
||||
autoload :CheckCreated, File.expand_path("../action/check_created", __FILE__)
|
||||
autoload :CheckGuestAdditions, File.expand_path("../action/check_guest_additions", __FILE__)
|
||||
autoload :CheckPortCollisions, File.expand_path("../action/check_port_collisions", __FILE__)
|
||||
autoload :CheckRunning, File.expand_path("../action/check_running", __FILE__)
|
||||
autoload :CheckVirtualbox, File.expand_path("../action/check_virtualbox", __FILE__)
|
||||
autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__)
|
||||
autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__)
|
||||
autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__)
|
||||
autoload :ClearSharedFolders, File.expand_path("../action/clear_shared_folders", __FILE__)
|
||||
autoload :Created, File.expand_path("../action/created", __FILE__)
|
||||
autoload :Customize, File.expand_path("../action/customize", __FILE__)
|
||||
autoload :DefaultName, File.expand_path("../action/default_name", __FILE__)
|
||||
autoload :Destroy, File.expand_path("../action/destroy", __FILE__)
|
||||
autoload :DestroyConfirm, File.expand_path("../action/destroy_confirm", __FILE__)
|
||||
autoload :DestroyUnusedNetworkInterfaces, File.expand_path("../action/destroy_unused_network_interfaces", __FILE__)
|
||||
autoload :DiscardState, File.expand_path("../action/discard_state", __FILE__)
|
||||
autoload :Export, File.expand_path("../action/export", __FILE__)
|
||||
autoload :ForwardPorts, File.expand_path("../action/forward_ports", __FILE__)
|
||||
autoload :Halt, File.expand_path("../action/halt", __FILE__)
|
||||
autoload :HostName, File.expand_path("../action/host_name", __FILE__)
|
||||
autoload :Import, File.expand_path("../action/import", __FILE__)
|
||||
autoload :IsRunning, File.expand_path("../action/is_running", __FILE__)
|
||||
autoload :IsSaved, File.expand_path("../action/is_saved", __FILE__)
|
||||
autoload :MatchMACAddress, File.expand_path("../action/match_mac_address", __FILE__)
|
||||
autoload :MessageNotCreated, File.expand_path("../action/message_not_created", __FILE__)
|
||||
autoload :MessageNotRunning, File.expand_path("../action/message_not_running", __FILE__)
|
||||
autoload :MessageWillNotDestroy, File.expand_path("../action/message_will_not_destroy", __FILE__)
|
||||
autoload :Network, File.expand_path("../action/network", __FILE__)
|
||||
autoload :NFS, File.expand_path("../action/nfs", __FILE__)
|
||||
autoload :Package, File.expand_path("../action/package", __FILE__)
|
||||
autoload :PackageVagrantfile, File.expand_path("../action/package_vagrantfile", __FILE__)
|
||||
autoload :Provision, File.expand_path("../action/provision", __FILE__)
|
||||
autoload :ProvisionerCleanup, File.expand_path("../action/provisioner_cleanup", __FILE__)
|
||||
autoload :PruneNFSExports, File.expand_path("../action/prune_nfs_exports", __FILE__)
|
||||
autoload :Resume, File.expand_path("../action/resume", __FILE__)
|
||||
autoload :SaneDefaults, File.expand_path("../action/sane_defaults", __FILE__)
|
||||
autoload :SetupPackageFiles, File.expand_path("../action/setup_package_files", __FILE__)
|
||||
autoload :ShareFolders, File.expand_path("../action/share_folders", __FILE__)
|
||||
autoload :Suspend, File.expand_path("../action/suspend", __FILE__)
|
||||
|
||||
# Include the built-in modules so that we can use them as top-level
|
||||
# things.
|
||||
include Vagrant::Action::Builtin
|
||||
|
||||
# This action boots the VM, assuming the VM is in a state that requires
|
||||
# a bootup (i.e. not saved).
|
||||
def self.action_boot
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckAccessible
|
||||
b.use CleanMachineFolder
|
||||
b.use ClearForwardedPorts
|
||||
b.use EnvSet, :port_collision_handler => :correct
|
||||
b.use ForwardPorts
|
||||
b.use Provision
|
||||
b.use PruneNFSExports
|
||||
b.use NFS
|
||||
b.use ClearSharedFolders
|
||||
b.use ShareFolders
|
||||
b.use ClearNetworkInterfaces
|
||||
b.use Network
|
||||
b.use HostName
|
||||
b.use SaneDefaults
|
||||
b.use Customize
|
||||
b.use Boot
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that is primarily responsible for completely
|
||||
# freeing the resources of the underlying virtual machine.
|
||||
def self.action_destroy
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env1, b2|
|
||||
if !env1[:result]
|
||||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use Call, DestroyConfirm do |env2, b3|
|
||||
if env2[:result]
|
||||
b3.use Vagrant::Action::General::Validate
|
||||
b3.use CheckAccessible
|
||||
b3.use EnvSet, :force => true
|
||||
b3.use action_halt
|
||||
b3.use ProvisionerCleanup
|
||||
b3.use PruneNFSExports
|
||||
b3.use Destroy
|
||||
b3.use CleanMachineFolder
|
||||
b3.use DestroyUnusedNetworkInterfaces
|
||||
else
|
||||
b3.use MessageWillNotDestroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that is primarily responsible for halting
|
||||
# the virtual machine, gracefully or by force.
|
||||
def self.action_halt
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env, b2|
|
||||
if env[:result]
|
||||
b2.use CheckAccessible
|
||||
b2.use DiscardState
|
||||
b2.use Halt
|
||||
else
|
||||
b2.use MessageNotCreated
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This action packages the virtual machine into a single box file.
|
||||
def self.action_package
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env1, b2|
|
||||
if !env1[:result]
|
||||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use SetupPackageFiles
|
||||
b2.use CheckAccessible
|
||||
b2.use action_halt
|
||||
b2.use ClearForwardedPorts
|
||||
b2.use ClearSharedFolders
|
||||
b2.use Export
|
||||
b2.use PackageVagrantfile
|
||||
b2.use Package
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This action just runs the provisioners on the machine.
|
||||
def self.action_provision
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Vagrant::Action::General::Validate
|
||||
b.use Call, Created do |env1, b2|
|
||||
if !env1[:result]
|
||||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use Call, IsRunning do |env2, b3|
|
||||
if !env2[:result]
|
||||
b3.use MessageNotRunning
|
||||
next
|
||||
end
|
||||
|
||||
b3.use CheckAccessible
|
||||
b3.use Provision
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This action is responsible for reloading the machine, which
|
||||
# brings it down, sucks in new configuration, and brings the
|
||||
# machine back up with the new configuration.
|
||||
def self.action_reload
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env1, b2|
|
||||
if !env1[:result]
|
||||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use Vagrant::Action::General::Validate
|
||||
b2.use action_halt
|
||||
b2.use action_start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that is primarily responsible for resuming
|
||||
# suspended machines.
|
||||
def self.action_resume
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env, b2|
|
||||
if env[:result]
|
||||
b2.use CheckAccessible
|
||||
b2.use CheckPortCollisions
|
||||
b2.use Resume
|
||||
else
|
||||
b2.use MessageNotCreated
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that will exec into an SSH shell.
|
||||
def self.action_ssh
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use CheckCreated
|
||||
b.use CheckAccessible
|
||||
b.use CheckRunning
|
||||
b.use SSHExec
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that will run a single SSH command.
|
||||
def self.action_ssh_run
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use CheckCreated
|
||||
b.use CheckAccessible
|
||||
b.use CheckRunning
|
||||
b.use SSHRun
|
||||
end
|
||||
end
|
||||
|
||||
# This action starts a VM, assuming it is already imported and exists.
|
||||
# A precondition of this action is that the VM exists.
|
||||
def self.action_start
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Vagrant::Action::General::Validate
|
||||
b.use Call, IsRunning do |env, b2|
|
||||
# If the VM is running, then our work here is done, exit
|
||||
next if env[:result]
|
||||
|
||||
b2.use Call, IsSaved do |env2, b3|
|
||||
if env2[:result]
|
||||
# The VM is saved, so just resume it
|
||||
b3.use action_resume
|
||||
else
|
||||
# The VM is not saved, so we must have to boot it up
|
||||
# like normal. Boot!
|
||||
b3.use action_boot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that is primarily responsible for suspending
|
||||
# the virtual machine.
|
||||
def self.action_suspend
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Call, Created do |env, b2|
|
||||
if env[:result]
|
||||
b2.use CheckAccessible
|
||||
b2.use Suspend
|
||||
else
|
||||
b2.use MessageNotCreated
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This action brings the machine up from nothing, including importing
|
||||
# the box, configuring metadata, and booting.
|
||||
def self.action_up
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use Vagrant::Action::General::Validate
|
||||
b.use Call, Created do |env, b2|
|
||||
# If the VM is NOT created yet, then do the setup steps
|
||||
if !env[:result]
|
||||
b2.use CheckAccessible
|
||||
b2.use CheckBox
|
||||
b2.use Import
|
||||
b2.use CheckGuestAdditions
|
||||
b2.use DefaultName
|
||||
b2.use MatchMACAddress
|
||||
end
|
||||
end
|
||||
b.use action_start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +1,27 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Boot
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env = env
|
||||
|
||||
# Start up the VM and wait for it to boot.
|
||||
boot
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.boot.booting")
|
||||
env[:machine].provider.driver.start(@env[:machine].config.vm.boot_mode)
|
||||
raise Errors::VMFailedToBoot if !wait_for_boot
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def boot
|
||||
@env[:ui].info I18n.t("vagrant.actions.vm.boot.booting")
|
||||
@env[:vm].driver.start(@env[:vm].config.vm.boot_mode)
|
||||
end
|
||||
|
||||
def wait_for_boot
|
||||
@env[:ui].info I18n.t("vagrant.actions.vm.boot.waiting")
|
||||
|
||||
@env[:vm].config.ssh.max_tries.to_i.times do |i|
|
||||
if @env[:vm].channel.ready?
|
||||
@env[:machine].config.ssh.max_tries.to_i.times do |i|
|
||||
if @env[:machine].communicate.ready?
|
||||
@env[:ui].info I18n.t("vagrant.actions.vm.boot.ready")
|
||||
return true
|
||||
end
|
||||
|
@ -37,7 +32,7 @@ module Vagrant
|
|||
|
||||
# If the VM is not starting or running, something went wrong
|
||||
# and we need to show a useful error.
|
||||
state = @env[:vm].state
|
||||
state = @env[:machine].provider.state
|
||||
raise Errors::VMFailedToRun if state != :starting && state != :running
|
||||
|
||||
sleep 2 if !@env["vagrant.test"]
|
||||
|
@ -46,8 +41,8 @@ module Vagrant
|
|||
@env[:ui].error I18n.t("vagrant.actions.vm.boot.failed")
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +1,18 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class CheckAccessible
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:vm].state == :inaccessible
|
||||
if env[:machine].state == :inaccessible
|
||||
# The VM we are attempting to manipulate is inaccessible. This
|
||||
# is a very bad situation and can only be fixed by the user. It
|
||||
# also prohibits us from actually doing anything with the virtual
|
||||
# machine, so we raise an error.
|
||||
raise Errors::VMInaccessible
|
||||
raise Vagrant::Errors::VMInaccessible
|
||||
end
|
||||
|
||||
@app.call(env)
|
|
@ -1,18 +1,18 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class CheckBox
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
box_name = env[:vm].config.vm.box
|
||||
raise Errors::BoxNotSpecified if !box_name
|
||||
box_name = env[:machine].config.vm.box
|
||||
raise Vagrant::Errors::BoxNotSpecified if !box_name
|
||||
|
||||
if !env[:box_collection].find(box_name, :virtualbox)
|
||||
box_url = env[:vm].config.vm.box_url
|
||||
raise Errors::BoxSpecifiedDoesntExist, :name => box_name if !box_url
|
||||
box_url = env[:machine].config.vm.box_url
|
||||
raise Vagrant::Errors::BoxSpecifiedDoesntExist, :name => box_name if !box_url
|
||||
|
||||
# Add the box then reload the box collection so that it becomes
|
||||
# aware of it.
|
||||
|
@ -21,8 +21,8 @@ module Vagrant
|
|||
env[:box_collection].reload!
|
||||
|
||||
# Reload the environment and set the VM to be the new loaded VM.
|
||||
env[:vm].env.reload!
|
||||
env[:vm] = env[:vm].env.vms[env[:vm].name]
|
||||
env[:machine].env.reload!
|
||||
env[:machine] = env[:machine].env.vms[env[:machine].name]
|
||||
end
|
||||
|
||||
@app.call(env)
|
|
@ -0,0 +1,21 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
# This middleware checks that the VM is created, and raises an exception
|
||||
# if it is not, notifying the user that creation is required.
|
||||
class CheckCreated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:machine].state == :not_created
|
||||
raise Vagrant::Errors::VMNotCreatedError
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Middleware which verifies that the VM has the proper guest additions
|
||||
# installed and prints a warning if they're not detected or if the
|
||||
# version does not match the installed VirtualBox version.
|
||||
class CheckGuestAdditions
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -12,13 +9,13 @@ module Vagrant
|
|||
def call(env)
|
||||
# Use the raw interface for now, while the virtualbox gem
|
||||
# doesn't support guest properties (due to cross platform issues)
|
||||
version = env[:vm].driver.read_guest_additions_version
|
||||
version = env[:machine].provider.driver.read_guest_additions_version
|
||||
if !version
|
||||
env[:ui].warn I18n.t("vagrant.actions.vm.check_guest_additions.not_detected")
|
||||
else
|
||||
# Strip the -OSE/_OSE off from the guest additions and the virtual box
|
||||
# version since all the matters are that the version _numbers_ match up.
|
||||
guest_version, vb_version = [version, env[:vm].driver.version].map do |v|
|
||||
guest_version, vb_version = [version, env[:machine].provider.driver.version].map do |v|
|
||||
v.gsub(/[-_]ose/i, '')
|
||||
end
|
||||
|
||||
|
@ -32,6 +29,7 @@ module Vagrant
|
|||
# Continue
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,12 +1,10 @@
|
|||
require "vagrant/util/is_port_open"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Action that checks to make sure there are no forwarded port collisions,
|
||||
# and raises an exception if there is.
|
||||
class CheckPortCollisions
|
||||
include Util::IsPortOpen
|
||||
include Vagrant::Util::IsPortOpen
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -22,12 +20,12 @@ module Vagrant
|
|||
# Read our forwarded ports, if we have any, to override what
|
||||
# we have configured.
|
||||
current = {}
|
||||
env[:vm].driver.read_forwarded_ports.each do |nic, name, hostport, guestport|
|
||||
env[:machine].provider.driver.read_forwarded_ports.each do |nic, name, hostport, guestport|
|
||||
current[name] = hostport.to_i
|
||||
end
|
||||
|
||||
existing = env[:vm].driver.read_used_ports
|
||||
env[:vm].config.vm.forwarded_ports.each do |options|
|
||||
existing = env[:machine].provider.driver.read_used_ports
|
||||
env[:machine].config.vm.forwarded_ports.each do |options|
|
||||
# Use the proper port, whether that be the configured port or the
|
||||
# port that is currently on use of the VM.
|
||||
hostport = options[:hostport].to_i
|
||||
|
@ -44,7 +42,7 @@ module Vagrant
|
|||
|
||||
# Handles a port collision by raising an exception.
|
||||
def handle_error(options, existing_ports)
|
||||
raise Errors::ForwardPortCollisionResume
|
||||
raise Vagrant::Errors::ForwardPortCollisionResume
|
||||
end
|
||||
|
||||
# Handles a port collision by attempting to fix it.
|
||||
|
@ -55,19 +53,21 @@ module Vagrant
|
|||
if !options[:auto]
|
||||
# Auto fixing is disabled for this port forward, so we
|
||||
# must throw an error so the user can fix it.
|
||||
raise Errors::ForwardPortCollision, :host_port => options[:hostport].to_s,
|
||||
raise Vagrant::Errors::ForwardPortCollision,
|
||||
:host_port => options[:hostport].to_s,
|
||||
:guest_port => options[:guestport].to_s
|
||||
end
|
||||
|
||||
# Get the auto port range and get rid of the used ports and
|
||||
# ports which are being used in other forwards so we're just
|
||||
# left with available ports.
|
||||
range = @env[:vm].config.vm.auto_port_range.to_a
|
||||
range -= @env[:vm].config.vm.forwarded_ports.collect { |opts| opts[:hostport].to_i }
|
||||
range = @env[:machine].config.vm.auto_port_range.to_a
|
||||
range -= @env[:machine].config.vm.forwarded_ports.collect { |opts| opts[:hostport].to_i }
|
||||
range -= existing_ports
|
||||
|
||||
if range.empty?
|
||||
raise Errors::ForwardPortAutolistEmpty, :vm_name => @env[:vm].name,
|
||||
raise Vagrant::Errors::ForwardPortAutolistEmpty,
|
||||
:vm_name => @env[:machine].name,
|
||||
:host_port => options[:hostport].to_s,
|
||||
:guest_port => options[:guestport].to_s
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
# This middleware checks that the VM is running, and raises an exception
|
||||
# if it is not, notifying the user that the VM must be running.
|
||||
class CheckRunning
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:machine].state != :running
|
||||
raise Vagrant::Errors::VMNotRunningError
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
# Checks that VirtualBox is installed and ready to be used.
|
||||
class CheckVirtualbox
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# This verifies that VirtualBox is installed and the driver is
|
||||
# ready to function. If not, then an exception will be raised
|
||||
# which will break us out of execution of the middleware sequence.
|
||||
Driver::Meta.new.verify!
|
||||
|
||||
# Carry on.
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,8 @@
|
|||
require 'fileutils'
|
||||
require "fileutils"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Cleans up the VirtualBox machine folder for any ".xml-prev"
|
||||
# files which VirtualBox may have left over. This is a bug in
|
||||
# VirtualBox. As soon as this is fixed, this middleware can and
|
||||
|
@ -13,7 +13,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
def call(env)
|
||||
clean_machine_folder(env[:vm].driver.read_machine_folder)
|
||||
clean_machine_folder(env[:machine].provider.driver.read_machine_folder)
|
||||
@app.call(env)
|
||||
end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class ClearForwardedPorts
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -8,7 +8,7 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting")
|
||||
env[:vm].driver.clear_forwarded_ports
|
||||
env[:machine].provider.driver.clear_forwarded_ports
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class ClearNetworkInterfaces
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -21,7 +21,7 @@ module Vagrant
|
|||
|
||||
# "Enable" all the adapters we setup.
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.clear_network_interfaces.deleting")
|
||||
env[:vm].driver.enable_adapters(adapters)
|
||||
env[:machine].provider.driver.enable_adapters(adapters)
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -1,14 +1,13 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class ClearSharedFolders
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:vm].driver.clear_shared_folders
|
||||
env[:machine].provider.driver.clear_shared_folders
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class Created
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Set the result to be true if the machine is created.
|
||||
env[:result] = env[:machine].state != :not_created
|
||||
|
||||
# Call the next if we have one (but we shouldn't, since this
|
||||
# middleware is built to run with the Call-type middlewares)
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,24 +1,24 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Customize
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
customizations = env[:vm].config.vm.customizations
|
||||
customizations = env[:machine].config.vm.customizations
|
||||
if !customizations.empty?
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.customize.running")
|
||||
|
||||
# Execute each customization command.
|
||||
customizations.each do |command|
|
||||
processed_command = command.collect do |arg|
|
||||
arg = env[:vm].uuid if arg == :id
|
||||
arg = env[:machine].id if arg == :id
|
||||
arg.to_s
|
||||
end
|
||||
|
||||
result = env[:vm].driver.execute_command(processed_command)
|
||||
result = env[:machine].provider.driver.execute_command(processed_command)
|
||||
if result.exit_code != 0
|
||||
raise Errors::VMCustomizationFailed, {
|
||||
:command => processed_command.inspect,
|
|
@ -1,8 +1,8 @@
|
|||
require 'log4r'
|
||||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class DefaultName
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::defaultname")
|
||||
|
@ -12,7 +12,7 @@ module Vagrant
|
|||
def call(env)
|
||||
@logger.info("Setting the default name of the VM")
|
||||
name = env[:root_path].basename.to_s + "_#{Time.now.to_i}"
|
||||
env[:vm].driver.set_name(name)
|
||||
env[:machine].provider.driver.set_name(name)
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Destroy
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -8,8 +8,8 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")
|
||||
env[:vm].driver.delete
|
||||
env[:vm].uuid = nil
|
||||
env[:machine].provider.driver.delete
|
||||
env[:machine].id = nil
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
require "vagrant/action/builtin/confirm"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class DestroyConfirm < Vagrant::Action::Builtin::Confirm
|
||||
def initialize(app, env)
|
||||
message = I18n.t("vagrant.commands.destroy.confirmation",
|
||||
:name => env[:machine].name)
|
||||
|
||||
super(app, env, message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class DestroyUnusedNetworkInterfaces
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:machine].provider.driver.delete_unused_host_only_networks
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class DiscardState
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:machine].provider.state == :saved
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding")
|
||||
env[:machine].provider.driver.discard_saved_state
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +1,19 @@
|
|||
require 'fileutils'
|
||||
require "fileutils"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Export
|
||||
attr_reader :temp_dir
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env = env
|
||||
|
||||
raise Errors::VMPowerOffToPackage if @env["vm"].state != :poweroff
|
||||
raise Vagrant::Errors::VMPowerOffToPackage if @env[:machine].provider.state != :poweroff
|
||||
|
||||
setup_temp_dir
|
||||
export
|
||||
|
@ -38,7 +37,7 @@ module Vagrant
|
|||
|
||||
def export
|
||||
@env[:ui].info I18n.t("vagrant.actions.vm.export.exporting")
|
||||
@env[:vm].driver.export(ovf_path) do |progress|
|
||||
@env[:machine].provider.driver.export(ovf_path) do |progress|
|
||||
@env[:ui].clear_line
|
||||
@env[:ui].report_progress(progress.percent, 100, false)
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class ForwardPorts
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -31,7 +31,7 @@ module Vagrant
|
|||
# organize them by their guestport, taking the "last one wins"
|
||||
# approach.
|
||||
guest_port_mapping = {}
|
||||
@env[:vm].config.vm.forwarded_ports.each do |options|
|
||||
@env[:machine].config.vm.forwarded_ports.each do |options|
|
||||
guest_port_mapping[options[:guestport]] = options
|
||||
end
|
||||
|
||||
|
@ -53,7 +53,7 @@ module Vagrant
|
|||
def forward_ports(mappings)
|
||||
ports = []
|
||||
|
||||
interfaces = @env[:vm].driver.read_network_interfaces
|
||||
interfaces = @env[:machine].provider.driver.read_network_interfaces
|
||||
|
||||
mappings.each do |options|
|
||||
message_attributes = {
|
||||
|
@ -83,7 +83,7 @@ module Vagrant
|
|||
|
||||
if !ports.empty?
|
||||
# We only need to forward ports if there are any to forward
|
||||
@env[:vm].driver.forward_ports(ports)
|
||||
@env[:machine].provider.driver.forward_ports(ports)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +1,25 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Halt
|
||||
def initialize(app, env, options=nil)
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
env.merge!(options || {})
|
||||
end
|
||||
|
||||
def call(env)
|
||||
current_state = env[:vm].state
|
||||
current_state = env[:machine].provider.state
|
||||
if current_state == :running || current_state == :gurumeditation
|
||||
# If the VM is running and we're not forcing, we can
|
||||
# attempt a graceful shutdown
|
||||
if current_state == :running && !env["force"]
|
||||
if current_state == :running && !env[:force]
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.halt.graceful")
|
||||
env[:vm].guest.halt
|
||||
env[:machine].guest.halt
|
||||
end
|
||||
|
||||
# If we're not powered off now, then force it
|
||||
if env[:vm].state != :poweroff
|
||||
if env[:machine].provider.state != :poweroff
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.halt.force")
|
||||
env[:vm].driver.halt
|
||||
env[:machine].provider.driver.halt
|
||||
end
|
||||
|
||||
# Sleep for a second to verify that the VM properly
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class HostName
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -9,10 +9,10 @@ module Vagrant
|
|||
def call(env)
|
||||
@app.call(env)
|
||||
|
||||
host_name = env[:vm].config.vm.host_name
|
||||
host_name = env[:machine].config.vm.host_name
|
||||
if !host_name.nil?
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.host_name.setting")
|
||||
env[:vm].guest.change_host_name(host_name)
|
||||
env[:machine].guest.change_host_name(host_name)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,22 +1,18 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Import
|
||||
# The name for easy reference
|
||||
def self.name
|
||||
:import
|
||||
end
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.import.importing", :name => env[:vm].box.name)
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.import.importing",
|
||||
:name => env[:machine].box.name)
|
||||
|
||||
# Import the virtual machine
|
||||
ovf_file = env[:vm].box.directory.join("box.ovf").to_s
|
||||
env[:vm].uuid = env[:vm].driver.import(ovf_file) do |progress|
|
||||
ovf_file = env[:machine].box.directory.join("box.ovf").to_s
|
||||
env[:machine].id = env[:machine].provider.driver.import(ovf_file) do |progress|
|
||||
env[:ui].clear_line
|
||||
env[:ui].report_progress(progress, 100, false)
|
||||
end
|
||||
|
@ -30,21 +26,21 @@ module Vagrant
|
|||
return if env[:interrupted]
|
||||
|
||||
# Flag as erroneous and return if import failed
|
||||
raise Errors::VMImportFailure if !env[:vm].uuid
|
||||
raise Vagrant::Errors::VMImportFailure if !env[:machine].id
|
||||
|
||||
# Import completed successfully. Continue the chain
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def recover(env)
|
||||
if env[:vm].created?
|
||||
return if env["vagrant.error"].is_a?(Errors::VagrantError)
|
||||
if env[:machine].provider.state != :not_created
|
||||
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
|
||||
|
||||
# Interrupted, destroy the VM. We note that we don't want to
|
||||
# validate the configuration here.
|
||||
destroy_env = env.clone
|
||||
destroy_env[:validate] = false
|
||||
env[:action_runner].run(:destroy, destroy_env)
|
||||
env[:action_runner].run(Action.action_destroy, destroy_env)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class IsRunning
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Set the result to be true if the machine is running.
|
||||
env[:result] = env[:machine].state == :running
|
||||
|
||||
# Call the next if we have one (but we shouldn't, since this
|
||||
# middleware is built to run with the Call-type middlewares)
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class IsSaved
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Set the result to be true if the machine is saved.
|
||||
env[:result] = env[:machine].state == :saved
|
||||
|
||||
# Call the next if we have one (but we shouldn't, since this
|
||||
# middleware is built to run with the Call-type middlewares)
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +1,17 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class MatchMACAddress
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
raise Errors::VMBaseMacNotSpecified if !env[:vm].config.vm.base_mac
|
||||
raise Vagrant::Errors::VMBaseMacNotSpecified if !env[:machine].config.vm.base_mac
|
||||
|
||||
# Create the proc which we want to use to modify the virtual machine
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.match_mac.matching")
|
||||
env[:vm].driver.set_mac_address(env[:vm].config.vm.base_mac)
|
||||
env[:machine].provider.driver.set_mac_address(env[:machine].config.vm.base_mac)
|
||||
|
||||
@app.call(env)
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class MessageNotCreated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.commands.common.vm_not_created")
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class MessageNotRunning
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.commands.common.vm_not_running")
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class MessageWillNotDestroy
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info I18n.t("vagrant.commands.destroy.will_not_destroy",
|
||||
:name => env[:machine].name)
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +1,15 @@
|
|||
require 'set'
|
||||
require "set"
|
||||
|
||||
require 'log4r'
|
||||
require "log4r"
|
||||
|
||||
require 'vagrant/util/network_ip'
|
||||
require "vagrant/util/network_ip"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# This action handles all `config.vm.network` configurations by
|
||||
# setting up the VM properly and enabling the networks afterword.
|
||||
class Network
|
||||
# Utilities to deal with network addresses
|
||||
include Util::NetworkIP
|
||||
include Vagrant::Util::NetworkIP
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::network")
|
||||
|
@ -28,7 +26,7 @@ module Vagrant
|
|||
@logger.debug("Determining adapters and networks...")
|
||||
adapters = []
|
||||
networks = []
|
||||
env[:vm].config.vm.networks.each do |type, args|
|
||||
env[:machine].config.vm.networks.each do |type, args|
|
||||
# Get the normalized configuration we'll use around
|
||||
config = send("#{type}_config", args)
|
||||
|
||||
|
@ -54,7 +52,7 @@ module Vagrant
|
|||
# Create all the network interfaces
|
||||
@logger.info("Enabling adapters...")
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.network.preparing")
|
||||
env[:vm].driver.enable_adapters(adapters)
|
||||
env[:machine].provider.driver.enable_adapters(adapters)
|
||||
end
|
||||
|
||||
# Continue the middleware chain. We're done with our VM
|
||||
|
@ -69,7 +67,7 @@ module Vagrant
|
|||
# want to configure the networks that have `auto_config` setup.
|
||||
networks_to_configure = networks.select { |n| n[:_auto_config] }
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.network.configuring")
|
||||
env[:vm].guest.configure_networks(networks_to_configure)
|
||||
env[:machine].guest.configure_networks(networks_to_configure)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -86,7 +84,7 @@ module Vagrant
|
|||
available = Set.new(1..8)
|
||||
|
||||
# Determine which NICs are actually available.
|
||||
interfaces = @env[:vm].driver.read_network_interfaces
|
||||
interfaces = @env[:machine].provider.driver.read_network_interfaces
|
||||
interfaces.each do |number, nic|
|
||||
# Remove the number from the available NICs if the
|
||||
# NIC is in use.
|
||||
|
@ -102,7 +100,7 @@ module Vagrant
|
|||
if !adapter[:adapter]
|
||||
# If we have no available adapters, then that is an exceptional
|
||||
# event.
|
||||
raise Errors::NetworkNoAdapters if available.empty?
|
||||
raise Vagrant::Errors::NetworkNoAdapters if available.empty?
|
||||
|
||||
# Otherwise, assign as the adapter the next available item
|
||||
adapter[:adapter] = available.shift
|
||||
|
@ -116,7 +114,7 @@ module Vagrant
|
|||
# Verify that there are no collisions in the adapters being used.
|
||||
used = Set.new
|
||||
adapters.each do |adapter|
|
||||
raise Errors::NetworkAdapterCollision if used.include?(adapter[:adapter])
|
||||
raise Vagrant::Errors::NetworkAdapterCollision if used.include?(adapter[:adapter])
|
||||
used.add(adapter[:adapter])
|
||||
end
|
||||
end
|
||||
|
@ -134,7 +132,7 @@ module Vagrant
|
|||
adapter_to_interface = {}
|
||||
|
||||
# Make a first pass to assign interface numbers by adapter location
|
||||
vm_adapters = @env[:vm].driver.read_network_interfaces
|
||||
vm_adapters = @env[:machine].provider.driver.read_network_interfaces
|
||||
vm_adapters.sort.each do |number, adapter|
|
||||
if adapter[:type] != :none
|
||||
# Not used, so assign the interface number and increment
|
||||
|
@ -220,7 +218,7 @@ module Vagrant
|
|||
# It is an error case if a specific name was given but the network
|
||||
# doesn't exist.
|
||||
if config[:name]
|
||||
raise Errors::NetworkNotFound, :name => config[:name]
|
||||
raise Vagrant::Errors::NetworkNotFound, :name => config[:name]
|
||||
end
|
||||
|
||||
# Otherwise, we create a new network and put the net network
|
||||
|
@ -238,13 +236,13 @@ module Vagrant
|
|||
interface[:dhcp][:lower] == config[:dhcp_lower] &&
|
||||
interface[:dhcp][:upper] == config[:dhcp_upper]
|
||||
|
||||
raise Errors::NetworkDHCPAlreadyAttached if !valid
|
||||
raise Vagrant::Errors::NetworkDHCPAlreadyAttached if !valid
|
||||
|
||||
@logger.debug("DHCP server already properly configured")
|
||||
else
|
||||
# Configure the DHCP server for the network.
|
||||
@logger.debug("Creating a DHCP server...")
|
||||
@env[:vm].driver.create_dhcp_server(interface[:name], config)
|
||||
@env[:machine].provider.driver.create_dhcp_server(interface[:name], config)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -274,7 +272,7 @@ module Vagrant
|
|||
options = config.dup
|
||||
options[:ip] = options[:adapter_ip]
|
||||
|
||||
@env[:vm].driver.create_host_only_network(options)
|
||||
@env[:machine].provider.driver.create_host_only_network(options)
|
||||
end
|
||||
|
||||
# Finds a host only network that matches our configuration on VirtualBox.
|
||||
|
@ -282,7 +280,7 @@ module Vagrant
|
|||
def find_matching_hostonly_network(config)
|
||||
this_netaddr = network_address(config[:ip], config[:netmask])
|
||||
|
||||
@env[:vm].driver.read_host_only_interfaces.each do |interface|
|
||||
@env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
|
||||
if config[:name] && config[:name] == interface[:name]
|
||||
return interface
|
||||
elsif this_netaddr == network_address(interface[:ip], interface[:netmask])
|
||||
|
@ -302,9 +300,9 @@ module Vagrant
|
|||
def verify_no_bridge_collision(options)
|
||||
this_netaddr = network_address(options[:ip], options[:netmask])
|
||||
|
||||
@env[:vm].driver.read_bridged_interfaces.each do |interface|
|
||||
@env[:machine].provider.driver.read_bridged_interfaces.each do |interface|
|
||||
that_netaddr = network_address(interface[:ip], interface[:netmask])
|
||||
raise Errors::NetworkCollision if this_netaddr == that_netaddr && interface[:status] != "Down"
|
||||
raise Vagrant::Errors::NetworkCollision if this_netaddr == that_netaddr && interface[:status] != "Down"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -323,7 +321,7 @@ module Vagrant
|
|||
|
||||
def bridged_adapter(config)
|
||||
# Find the bridged interfaces that are available
|
||||
bridgedifs = @env[:vm].driver.read_bridged_interfaces
|
||||
bridgedifs = @env[:machine].provider.driver.read_bridged_interfaces
|
||||
bridgedifs.delete_if { |interface| interface[:status] == "Down" }
|
||||
|
||||
# The name of the chosen bridge interface will be assigned to this
|
|
@ -4,21 +4,9 @@ require 'pathname'
|
|||
|
||||
require 'log4r'
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Enables NFS based shared folders. `nfsd` must already be installed
|
||||
# on the host machine, and NFS client must already be installed on
|
||||
# the guest machine.
|
||||
#
|
||||
# This is a two part process:
|
||||
#
|
||||
# 1. Adds an entry to `/etc/exports` on the host machine using
|
||||
# the host class to export the proper folder to the proper
|
||||
# machine.
|
||||
# 2. After boot, runs `mount` on the guest to mount the shared
|
||||
# folder.
|
||||
#
|
||||
class NFS
|
||||
def initialize(app,env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
|
||||
|
@ -54,7 +42,7 @@ module Vagrant
|
|||
def extract_folders
|
||||
# Load the NFS enabled shared folders
|
||||
@folders = {}
|
||||
@env[:vm].config.vm.shared_folders.each do |key, opts|
|
||||
@env[:machine].config.vm.shared_folders.each do |key, opts|
|
||||
if opts[:nfs]
|
||||
# Duplicate the options, set the hostpath, and set disabled on the original
|
||||
# options so the ShareFolders middleware doesn't try to mount it.
|
||||
|
@ -68,7 +56,8 @@ module Vagrant
|
|||
begin
|
||||
FileUtils.mkpath(hostpath)
|
||||
rescue Errno::EACCES
|
||||
raise Errors::SharedFolderCreateFailed, :path => hostpath.to_s
|
||||
raise Vagrant::Errors::SharedFolderCreateFailed,
|
||||
:path => hostpath.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -112,7 +101,7 @@ module Vagrant
|
|||
|
||||
# The options on the hash get priority, then the default
|
||||
# values
|
||||
value = opts.has_key?(key) ? opts[key] : @env[:vm].config.nfs.send(key)
|
||||
value = opts.has_key?(key) ? opts[key] : @env[:machine].config.nfs.send(key)
|
||||
return value if value != :auto
|
||||
|
||||
# Get UID/GID from folder if we've made it this far
|
||||
|
@ -126,7 +115,7 @@ module Vagrant
|
|||
# up to the host class to define the specific behavior.
|
||||
def export_folders
|
||||
@env[:ui].info I18n.t("vagrant.actions.vm.nfs.exporting")
|
||||
@env[:host].nfs_export(@env[:vm].uuid, guest_ip, folders)
|
||||
@env[:host].nfs_export(@env[:machine].id, guest_ip, folders)
|
||||
end
|
||||
|
||||
# Uses the system class to mount the NFS folders.
|
||||
|
@ -141,16 +130,16 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
@env[:vm].guest.mount_nfs(host_ip, mount_folders)
|
||||
@env[:machine].guest.mount_nfs(host_ip, mount_folders)
|
||||
end
|
||||
|
||||
# Returns the IP address of the first host only network adapter
|
||||
#
|
||||
# @return [String]
|
||||
def host_ip
|
||||
@env[:vm].driver.read_network_interfaces.each do |adapter, opts|
|
||||
@env[:machine].provider.driver.read_network_interfaces.each do |adapter, opts|
|
||||
if opts[:type] == :hostonly
|
||||
@env[:vm].driver.read_host_only_interfaces.each do |interface|
|
||||
@env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
|
||||
if interface[:name] == opts[:hostonly]
|
||||
return interface[:ip]
|
||||
end
|
||||
|
@ -166,7 +155,7 @@ module Vagrant
|
|||
#
|
||||
# @return [String]
|
||||
def guest_ip
|
||||
@env[:vm].config.vm.networks.each do |type, args|
|
||||
@env[:machine].config.vm.networks.each do |type, args|
|
||||
if type == :hostonly && args[0].is_a?(String)
|
||||
return args[0]
|
||||
end
|
||||
|
@ -177,7 +166,7 @@ module Vagrant
|
|||
|
||||
# Checks if there are any NFS enabled shared folders.
|
||||
def nfs_enabled?
|
||||
@env[:vm].config.vm.shared_folders.each do |key, opts|
|
||||
@env[:machine].config.vm.shared_folders.each do |key, opts|
|
||||
return true if opts[:nfs]
|
||||
end
|
||||
|
||||
|
@ -186,9 +175,9 @@ module Vagrant
|
|||
|
||||
# Verifies that the host is set and supports NFS.
|
||||
def verify_settings
|
||||
raise Errors::NFSHostRequired if @env[:host].nil?
|
||||
raise Errors::NFSNotSupported if !@env[:host].nfs?
|
||||
raise Errors::NFSNoHostNetwork if !guest_ip
|
||||
raise Vagrant::Errors::NFSHostRequired if @env[:host].nil?
|
||||
raise Vagrant::Errors::NFSNotSupported if !@env[:host].nfs?
|
||||
raise Vagrant::Errors::NFSNoHostNetwork if !guest_ip
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,12 +1,9 @@
|
|||
require 'vagrant/action/general/package'
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# A subclass of {General::Package} which simply makes sure that
|
||||
# the package directory is set to the directory which the VM
|
||||
# was exported to.
|
||||
class Package < General::Package
|
||||
class Package < Vagrant::Action::General::Package
|
||||
# Doing this so that we can test that the parent is properly
|
||||
# called in the unit tests.
|
||||
alias_method :general_call, :call
|
|
@ -1,17 +1,14 @@
|
|||
require 'vagrant/util/template_renderer'
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Puts a generated Vagrantfile into the package directory so that
|
||||
# it can be included in the package.
|
||||
class PackageVagrantfile
|
||||
# For TemplateRenderer
|
||||
include Util
|
||||
include Vagrant::Util
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@env = env
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
@ -26,7 +23,7 @@ module Vagrant
|
|||
def create_vagrantfile
|
||||
File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f|
|
||||
f.write(TemplateRenderer.render("package_Vagrantfile", {
|
||||
:base_mac => @env["vm"].driver.read_mac_address
|
||||
:base_mac => @env[:machine].provider.driver.read_mac_address
|
||||
}))
|
||||
end
|
||||
end
|
|
@ -1,8 +1,8 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Provision
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::provision")
|
||||
|
@ -19,6 +19,7 @@ module Vagrant
|
|||
# We set this here so that even if this value is changed in the future,
|
||||
# it stays constant to what we expect here in this moment.
|
||||
enabled = env["provision.enabled"]
|
||||
|
||||
# Instantiate and prepare the provisioners. Preparation must happen here
|
||||
# so that shared folders and such can properly take effect.
|
||||
provisioners = enabled_provisioners
|
||||
|
@ -38,7 +39,7 @@ module Vagrant
|
|||
|
||||
def enabled_provisioners
|
||||
enabled = []
|
||||
@env[:vm].config.vm.provisioners.each do |provisioner|
|
||||
@env[:machine].config.vm.provisioners.each do |provisioner|
|
||||
if @env["provision.types"]
|
||||
# If we've specified types of provisioners to enable, then we
|
||||
# only use those provisioners, and skip any that we haven't
|
|
@ -0,0 +1,25 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class ProvisionerCleanup
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Instantiate all the enabled provisioners
|
||||
provisioners = env[:machine].config.vm.provisioners.map do |provisioner|
|
||||
provisioner.provisioner.new(env, provisioner.config)
|
||||
end
|
||||
|
||||
# Call cleanup on each
|
||||
provisioners.each do |instance|
|
||||
instance.cleanup
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class PruneNFSExports
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -8,7 +8,7 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
if env[:host]
|
||||
valid_ids = env[:vm].driver.read_vms
|
||||
valid_ids = env[:machine].provider.driver.read_vms
|
||||
env[:host].nfs_prune(valid_ids)
|
||||
end
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Resume
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:vm].state == :saved
|
||||
if env[:machine].provider.state == :saved
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming")
|
||||
env[:action_runner].run(Boot, env)
|
||||
end
|
|
@ -1,11 +1,8 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# This middleware enforces some sane defaults on the virtualbox
|
||||
# VM which help with performance, stability, and in some cases
|
||||
# behavior.
|
||||
class SaneDefaults
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::sanedefaults")
|
||||
|
@ -22,7 +19,7 @@ module Vagrant
|
|||
# errors. The Host IO cache vastly improves disk IO performance
|
||||
# for VMs.
|
||||
command = [
|
||||
"storagectl", env[:vm].uuid,
|
||||
"storagectl", env[:machine].id,
|
||||
"--name", "SATA Controller",
|
||||
"--hostiocache", "on"
|
||||
]
|
||||
|
@ -46,14 +43,14 @@ module Vagrant
|
|||
# Enable/disable the NAT DNS proxy as necessary
|
||||
if enable_dns_proxy
|
||||
command = [
|
||||
"modifyvm", env[:vm].uuid,
|
||||
"modifyvm", env[:machine].id,
|
||||
"--natdnsproxy1", "on"
|
||||
]
|
||||
attempt_and_log(command, "Enable the NAT DNS proxy on adapter 1...")
|
||||
else
|
||||
command = [ "modifyvm", env[:vm].uuid, "--natdnsproxy1", "off" ]
|
||||
command = [ "modifyvm", env[:machine].id, "--natdnsproxy1", "off" ]
|
||||
attempt_and_log(command, "Disable the NAT DNS proxy on adapter 1...")
|
||||
command = [ "modifyvm", env[:vm].uuid, "--natdnshostresolver1", "off" ]
|
||||
command = [ "modifyvm", env[:machine].id, "--natdnshostresolver1", "off" ]
|
||||
attempt_and_log(command, "Disable the NAT DNS resolver on adapter 1...")
|
||||
end
|
||||
|
||||
|
@ -69,7 +66,7 @@ module Vagrant
|
|||
# @param [Array] command Command to run
|
||||
# @param [String] log Log message to write.
|
||||
def attempt_and_log(command, log)
|
||||
result = @env[:vm].driver.execute_command(command)
|
||||
result = @env[:machine].provider.driver.execute_command(command)
|
||||
@logger.info("#{log} (exit status = #{result.exit_code})")
|
||||
end
|
||||
end
|
|
@ -1,10 +1,6 @@
|
|||
require 'pathname'
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
# Sets up the mapping of files to copy into the package and verifies
|
||||
# that the files can be properly copied.
|
||||
class SetupPackageFiles
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
|
@ -40,7 +36,8 @@ module Vagrant
|
|||
|
||||
# Verify the mapping
|
||||
files.each do |from, _|
|
||||
raise Errors::PackageIncludeMissing, :file => from if !File.exist?(from)
|
||||
raise Vagrant::Errors::PackageIncludeMissing,
|
||||
:file => from if !File.exist?(from)
|
||||
end
|
||||
|
||||
# Save the mapping
|
|
@ -1,10 +1,10 @@
|
|||
require 'pathname'
|
||||
require "pathname"
|
||||
|
||||
require 'log4r'
|
||||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class ShareFolders
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::share_folders")
|
||||
|
@ -25,7 +25,7 @@ module Vagrant
|
|||
# This method returns an actual list of VirtualBox shared
|
||||
# folders to create and their proper path.
|
||||
def shared_folders
|
||||
@env[:vm].config.vm.shared_folders.inject({}) do |acc, data|
|
||||
@env[:machine].config.vm.shared_folders.inject({}) do |acc, data|
|
||||
key, value = data
|
||||
|
||||
next acc if value[:disabled]
|
||||
|
@ -50,7 +50,8 @@ module Vagrant
|
|||
begin
|
||||
hostpath.mkpath
|
||||
rescue Errno::EACCES
|
||||
raise Errors::SharedFolderCreateFailed, :path => hostpath.to_s
|
||||
raise Vagrant::Errors::SharedFolderCreateFailed,
|
||||
:path => hostpath.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -68,7 +69,7 @@ module Vagrant
|
|||
}
|
||||
end
|
||||
|
||||
@env[:vm].driver.share_folders(folders)
|
||||
@env[:machine].provider.driver.share_folders(folders)
|
||||
end
|
||||
|
||||
def mount_shared_folders
|
||||
|
@ -96,11 +97,11 @@ module Vagrant
|
|||
data = data.dup
|
||||
|
||||
# Calculate the owner and group
|
||||
data[:owner] ||= @env[:vm].config.ssh.username
|
||||
data[:group] ||= @env[:vm].config.ssh.username
|
||||
data[:owner] ||= @env[:machine].config.ssh.username
|
||||
data[:group] ||= @env[:machine].config.ssh.username
|
||||
|
||||
# Mount the actual folder
|
||||
@env[:vm].guest.mount_shared_folder(name, data[:guestpath], data)
|
||||
@env[:machine].guest.mount_shared_folder(name, data[:guestpath], data)
|
||||
else
|
||||
# If no guest path is specified, then automounting is disabled
|
||||
@env[:ui].info(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
|
|
@ -1,15 +1,15 @@
|
|||
module Vagrant
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
module VM
|
||||
class Suspend
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if env[:vm].state == :running
|
||||
if env[:machine].provider.state == :running
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending")
|
||||
env[:vm].driver.suspend
|
||||
env[:machine].provider.driver.suspend
|
||||
end
|
||||
|
||||
@app.call(env)
|
|
@ -0,0 +1,327 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/util/busy'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/retryable'
|
||||
require 'vagrant/util/subprocess'
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Driver
|
||||
# Base class for all VirtualBox drivers.
|
||||
#
|
||||
# This class provides useful tools for things such as executing
|
||||
# VBoxManage and handling SIGINTs and so on.
|
||||
class Base
|
||||
# Include this so we can use `Subprocess` more easily.
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def initialize
|
||||
@logger = Log4r::Logger.new("vagrant::provider::virtualbox::base")
|
||||
|
||||
# This flag is used to keep track of interrupted state (SIGINT)
|
||||
@interrupted = false
|
||||
|
||||
# Set the path to VBoxManage
|
||||
@vboxmanage_path = "VBoxManage"
|
||||
|
||||
if Vagrant::Util::Platform.windows?
|
||||
@logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage")
|
||||
|
||||
# On Windows, we use the VBOX_INSTALL_PATH environmental
|
||||
# variable to find VBoxManage.
|
||||
if ENV.has_key?("VBOX_INSTALL_PATH")
|
||||
# Get the path.
|
||||
path = ENV["VBOX_INSTALL_PATH"]
|
||||
@logger.debug("VBOX_INSTALL_PATH value: #{path}")
|
||||
|
||||
# There can actually be multiple paths in here, so we need to
|
||||
# split by the separator ";" and see which is a good one.
|
||||
path.split(";").each do |single|
|
||||
# Make sure it ends with a \
|
||||
single += "\\" if !single.end_with?("\\")
|
||||
|
||||
# If the executable exists, then set it as the main path
|
||||
# and break out
|
||||
vboxmanage = "#{path}VBoxManage.exe"
|
||||
if File.file?(vboxmanage)
|
||||
@vboxmanage_path = vboxmanage
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@logger.info("VBoxManage path: #{@vboxmanage_path}")
|
||||
end
|
||||
|
||||
# Clears the forwarded ports that have been set on the virtual machine.
|
||||
def clear_forwarded_ports
|
||||
end
|
||||
|
||||
# Clears the shared folders that have been set on the virtual machine.
|
||||
def clear_shared_folders
|
||||
end
|
||||
|
||||
# Creates a DHCP server for a host only network.
|
||||
#
|
||||
# @param [String] network Name of the host-only network.
|
||||
# @param [Hash] options Options for the DHCP server.
|
||||
def create_dhcp_server(network, options)
|
||||
end
|
||||
|
||||
# Creates a host only network with the given options.
|
||||
#
|
||||
# @param [Hash] options Options to create the host only network.
|
||||
# @return [Hash] The details of the host only network, including
|
||||
# keys `:name`, `:ip`, and `:netmask`
|
||||
def create_host_only_network(options)
|
||||
end
|
||||
|
||||
# Deletes the virtual machine references by this driver.
|
||||
def delete
|
||||
end
|
||||
|
||||
# Deletes any host only networks that aren't being used for anything.
|
||||
def delete_unused_host_only_networks
|
||||
end
|
||||
|
||||
# Discards any saved state associated with this VM.
|
||||
def discard_saved_state
|
||||
end
|
||||
|
||||
# Enables network adapters on the VM.
|
||||
#
|
||||
# The format of each adapter specification should be like so:
|
||||
#
|
||||
# {
|
||||
# :type => :hostonly,
|
||||
# :hostonly => "vboxnet0",
|
||||
# :mac_address => "tubes"
|
||||
# }
|
||||
#
|
||||
# This must support setting up both host only and bridged networks.
|
||||
#
|
||||
# @param [Array<Hash>] adapters Array of adapters to enable.
|
||||
def enable_adapters(adapters)
|
||||
end
|
||||
|
||||
# Execute a raw command straight through to VBoxManage.
|
||||
#
|
||||
# @param [Array] command Command to execute.
|
||||
def execute_command(command)
|
||||
end
|
||||
|
||||
# Exports the virtual machine to the given path.
|
||||
#
|
||||
# @param [String] path Path to the OVF file.
|
||||
# @yield [progress] Yields the block with the progress of the export.
|
||||
def export(path)
|
||||
end
|
||||
|
||||
# Forwards a set of ports for a VM.
|
||||
#
|
||||
# This will not affect any previously set forwarded ports,
|
||||
# so be sure to delete those if you need to.
|
||||
#
|
||||
# The format of each port hash should be the following:
|
||||
#
|
||||
# {
|
||||
# :name => "foo",
|
||||
# :hostport => 8500,
|
||||
# :guestport => 80,
|
||||
# :adapter => 1,
|
||||
# :protocol => "tcp"
|
||||
# }
|
||||
#
|
||||
# Note that "adapter" and "protocol" are optional and will default
|
||||
# to 1 and "tcp" respectively.
|
||||
#
|
||||
# @param [Array<Hash>] ports An array of ports to set. See documentation
|
||||
# for more information on the format.
|
||||
def forward_ports(ports)
|
||||
end
|
||||
|
||||
# Halts the virtual machine (pulls the plug).
|
||||
def halt
|
||||
end
|
||||
|
||||
# Imports the VM from an OVF file.
|
||||
#
|
||||
# @param [String] ovf Path to the OVF file.
|
||||
# @return [String] UUID of the imported VM.
|
||||
def import(ovf)
|
||||
end
|
||||
|
||||
# Returns a list of forwarded ports for a VM.
|
||||
#
|
||||
# @param [String] uuid UUID of the VM to read from, or `nil` if this
|
||||
# VM.
|
||||
# @param [Boolean] active_only If true, only VMs that are running will
|
||||
# be checked.
|
||||
# @return [Array<Array>]
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
end
|
||||
|
||||
# Returns a list of bridged interfaces.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_bridged_interfaces
|
||||
end
|
||||
|
||||
# Returns the guest additions version that is installed on this VM.
|
||||
#
|
||||
# @return [String]
|
||||
def read_guest_additions_version
|
||||
end
|
||||
|
||||
# Returns a list of available host only interfaces.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_host_only_interfaces
|
||||
end
|
||||
|
||||
# Returns the MAC address of the first network interface.
|
||||
#
|
||||
# @return [String]
|
||||
def read_mac_address
|
||||
end
|
||||
|
||||
# Returns the folder where VirtualBox places it's VMs.
|
||||
#
|
||||
# @return [String]
|
||||
def read_machine_folder
|
||||
end
|
||||
|
||||
# Returns a list of network interfaces of the VM.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_network_interfaces
|
||||
end
|
||||
|
||||
# Returns the current state of this VM.
|
||||
#
|
||||
# @return [Symbol]
|
||||
def read_state
|
||||
end
|
||||
|
||||
# Returns a list of all forwarded ports in use by active
|
||||
# virtual machines.
|
||||
#
|
||||
# @return [Array]
|
||||
def read_used_ports
|
||||
end
|
||||
|
||||
# Returns a list of all UUIDs of virtual machines currently
|
||||
# known by VirtualBox.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
def read_vms
|
||||
end
|
||||
|
||||
# Sets the MAC address of the first network adapter.
|
||||
#
|
||||
# @param [String] mac MAC address without any spaces/hyphens.
|
||||
def set_mac_address(mac)
|
||||
end
|
||||
|
||||
# Share a set of folders on this VM.
|
||||
#
|
||||
# @param [Array<Hash>] folders
|
||||
def share_folders(folders)
|
||||
end
|
||||
|
||||
# Reads the SSH port of this VM.
|
||||
#
|
||||
# @param [Integer] expected Expected guest port of SSH.
|
||||
def ssh_port(expected)
|
||||
end
|
||||
|
||||
# Starts the virtual machine.
|
||||
#
|
||||
# @param [String] mode Mode to boot the VM. Either "headless"
|
||||
# or "gui"
|
||||
def start(mode)
|
||||
end
|
||||
|
||||
# Suspend the virtual machine.
|
||||
def suspend
|
||||
end
|
||||
|
||||
# Verifies that the driver is ready to accept work.
|
||||
#
|
||||
# This should raise a VagrantError if things are not ready.
|
||||
def verify!
|
||||
end
|
||||
|
||||
# Verifies that an image can be imported properly.
|
||||
#
|
||||
# @param [String] path Path to an OVF file.
|
||||
# @return [Boolean]
|
||||
def verify_image(path)
|
||||
end
|
||||
|
||||
# Checks if a VM with the given UUID exists.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def vm_exists?(uuid)
|
||||
end
|
||||
|
||||
# Execute the given subcommand for VBoxManage and return the output.
|
||||
def execute(*command, &block)
|
||||
# Get the options hash if it exists
|
||||
opts = {}
|
||||
opts = command.pop if command.last.is_a?(Hash)
|
||||
|
||||
tries = 0
|
||||
tries = 3 if opts[:retryable]
|
||||
|
||||
# Variable to store our execution result
|
||||
r = nil
|
||||
|
||||
retryable(:on => Vagrant::Errors::VBoxManageError, :tries => tries, :sleep => 1) do
|
||||
# Execute the command
|
||||
r = raw(*command, &block)
|
||||
|
||||
# If the command was a failure, then raise an exception that is
|
||||
# nicely handled by Vagrant.
|
||||
if r.exit_code != 0
|
||||
if @interrupted
|
||||
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
||||
else
|
||||
raise Vagrant::Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
else
|
||||
# Sometimes, VBoxManage fails but doesn't actual return a non-zero
|
||||
# exit code. For this we inspect the output and determine if an error
|
||||
# occurred.
|
||||
if r.stderr =~ /VBoxManage: error:/
|
||||
@logger.info("VBoxManage error text found, assuming error.")
|
||||
raise Vagrant::Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the output, making sure to replace any Windows-style
|
||||
# newlines with Unix-style.
|
||||
r.stdout.gsub("\r\n", "\n")
|
||||
end
|
||||
|
||||
# Executes a command and returns the raw result object.
|
||||
def raw(*command, &block)
|
||||
int_callback = lambda do
|
||||
@interrupted = true
|
||||
@logger.info("Interrupted.")
|
||||
end
|
||||
|
||||
# Append in the options for subprocess
|
||||
command << { :notify => [:stdout, :stderr] }
|
||||
|
||||
Vagrant::Util::Busy.busy(int_callback) do
|
||||
Vagrant::Util::Subprocess.execute(@vboxmanage_path, *command, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,139 @@
|
|||
require "forwardable"
|
||||
|
||||
require "log4r"
|
||||
|
||||
require File.expand_path("../base", __FILE__)
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Driver
|
||||
class Meta < Base
|
||||
# This is raised if the VM is not found when initializing a driver
|
||||
# with a UUID.
|
||||
class VMNotFound < StandardError; end
|
||||
|
||||
# We use forwardable to do all our driver forwarding
|
||||
extend Forwardable
|
||||
|
||||
# The UUID of the virtual machine we represent
|
||||
attr_reader :uuid
|
||||
|
||||
# The version of virtualbox that is running.
|
||||
attr_reader :version
|
||||
|
||||
def initialize(uuid=nil)
|
||||
# Setup the base
|
||||
super()
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::provider::virtualbox::meta")
|
||||
@uuid = uuid
|
||||
|
||||
# Read and assign the version of VirtualBox we know which
|
||||
# specific driver to instantiate.
|
||||
begin
|
||||
@version = read_version || ""
|
||||
rescue Vagrant::Util::Subprocess::LaunchError
|
||||
# This means that VirtualBox was not found, so we raise this
|
||||
# error here.
|
||||
raise Vagrant::Errors::VirtualBoxNotDetected
|
||||
end
|
||||
|
||||
# Instantiate the proper version driver for VirtualBox
|
||||
@logger.debug("Finding driver for VirtualBox version: #{@version}")
|
||||
driver_map = {
|
||||
"4.0" => Version_4_0,
|
||||
"4.1" => Version_4_1
|
||||
}
|
||||
|
||||
driver_klass = nil
|
||||
driver_map.each do |key, klass|
|
||||
if @version.start_with?(key)
|
||||
driver_klass = klass
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if !driver_klass
|
||||
supported_versions = driver_map.keys.sort.join(", ")
|
||||
raise Vagrant::Errors::VirtualBoxInvalidVersion, :supported_versions => supported_versions
|
||||
end
|
||||
|
||||
@logger.info("Using VirtualBox driver: #{driver_klass}")
|
||||
@driver = driver_klass.new(@uuid)
|
||||
|
||||
if @uuid
|
||||
# Verify the VM exists, and if it doesn't, then don't worry
|
||||
# about it (mark the UUID as nil)
|
||||
raise VMNotFound if !@driver.vm_exists?(@uuid)
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :@driver, :clear_forwarded_ports,
|
||||
:clear_shared_folders,
|
||||
:create_dhcp_server,
|
||||
:create_host_only_network,
|
||||
:delete,
|
||||
:delete_unused_host_only_networks,
|
||||
:discard_saved_state,
|
||||
:enable_adapters,
|
||||
:execute_command,
|
||||
:export,
|
||||
:forward_ports,
|
||||
:halt,
|
||||
:import,
|
||||
:read_forwarded_ports,
|
||||
:read_bridged_interfaces,
|
||||
:read_guest_additions_version,
|
||||
:read_host_only_interfaces,
|
||||
:read_mac_address,
|
||||
:read_mac_addresses,
|
||||
:read_machine_folder,
|
||||
:read_network_interfaces,
|
||||
:read_state,
|
||||
:read_used_ports,
|
||||
:read_vms,
|
||||
:set_mac_address,
|
||||
:set_name,
|
||||
:share_folders,
|
||||
:ssh_port,
|
||||
:start,
|
||||
:suspend,
|
||||
:verify!,
|
||||
:verify_image,
|
||||
:vm_exists?
|
||||
|
||||
protected
|
||||
|
||||
# This returns the version of VirtualBox that is running.
|
||||
#
|
||||
# @return [String]
|
||||
def read_version
|
||||
# The version string is usually in one of the following formats:
|
||||
#
|
||||
# * 4.1.8r1234
|
||||
# * 4.1.8r1234_OSE
|
||||
# * 4.1.8_MacPortsr1234
|
||||
#
|
||||
# Below accounts for all of these.
|
||||
|
||||
# Note: We split this into multiple lines because apparently "".split("_")
|
||||
# is [], so we have to check for an empty array in between.
|
||||
output = execute("--version")
|
||||
if output =~ /vboxdrv kernel module is not loaded/
|
||||
raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded
|
||||
elsif output =~ /Please install/
|
||||
# Check for installation incomplete warnings, for example:
|
||||
# "WARNING: The character device /dev/vboxdrv does not
|
||||
# exist. Please install the virtualbox-ose-dkms package and
|
||||
# the appropriate headers, most likely linux-headers-generic."
|
||||
raise Vagrant::Errors::VirtualBoxInstallIncomplete
|
||||
end
|
||||
|
||||
parts = output.split("_")
|
||||
return nil if parts.empty?
|
||||
parts[0].split("r")[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue