Remove the builtin VM middleware that are now part of the VB provider
This commit is contained in:
parent
b659191a02
commit
ef5eabb63d
|
@ -1,53 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
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[:ui].info I18n.t("vagrant.actions.vm.boot.ready")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return true so that the vm_failed_to_boot error doesn't
|
|
||||||
# get shown
|
|
||||||
return true if @env[:interrupted]
|
|
||||||
|
|
||||||
# If the VM is not starting or running, something went wrong
|
|
||||||
# and we need to show a useful error.
|
|
||||||
state = @env[:vm].state
|
|
||||||
raise Errors::VMFailedToRun if state != :starting && state != :running
|
|
||||||
|
|
||||||
sleep 2 if !@env["vagrant.test"]
|
|
||||||
end
|
|
||||||
|
|
||||||
@env[:ui].error I18n.t("vagrant.actions.vm.boot.failed")
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Add the box then reload the box collection so that it becomes
|
|
||||||
# aware of it.
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.check_box.not_found", :name => box_name)
|
|
||||||
env[:box_collection].add(box_name, box_url)
|
|
||||||
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]
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,38 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
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|
|
|
||||||
v.gsub(/[-_]ose/i, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
if guest_version != vb_version
|
|
||||||
env[:ui].warn(I18n.t("vagrant.actions.vm.check_guest_additions.version_mismatch",
|
|
||||||
:guest_version => version,
|
|
||||||
:virtualbox_version => vb_version))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Continue
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,89 +0,0 @@
|
||||||
require "vagrant/util/is_port_open"
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
# For the handlers...
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
# Figure out how we handle port collisions. By default we error.
|
|
||||||
handler = env[:port_collision_handler] || :error
|
|
||||||
|
|
||||||
# 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|
|
|
||||||
current[name] = hostport.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
existing = env[:vm].driver.read_used_ports
|
|
||||||
env[:vm].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
|
|
||||||
hostport = current[options[:name]] if current.has_key?(options[:name])
|
|
||||||
|
|
||||||
if existing.include?(hostport) || is_port_open?("localhost", hostport)
|
|
||||||
# We have a collision! Handle it
|
|
||||||
send("handle_#{handler}".to_sym, options, existing)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handles a port collision by raising an exception.
|
|
||||||
def handle_error(options, existing_ports)
|
|
||||||
raise Errors::ForwardPortCollisionResume
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handles a port collision by attempting to fix it.
|
|
||||||
def handle_correct(options, existing_ports)
|
|
||||||
# We need to keep this for messaging purposes
|
|
||||||
original_hostport = options[:hostport]
|
|
||||||
|
|
||||||
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,
|
|
||||||
: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 -= existing_ports
|
|
||||||
|
|
||||||
if range.empty?
|
|
||||||
raise Errors::ForwardPortAutolistEmpty, :vm_name => @env[:vm].name,
|
|
||||||
:host_port => options[:hostport].to_s,
|
|
||||||
:guest_port => options[:guestport].to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the port up to be the first one and add that port to
|
|
||||||
# the used list.
|
|
||||||
options[:hostport] = range.shift
|
|
||||||
existing_ports << options[:hostport]
|
|
||||||
|
|
||||||
# Notify the user
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision",
|
|
||||||
:host_port => original_hostport.to_s,
|
|
||||||
:guest_port => options[:guestport].to_s,
|
|
||||||
:new_port => options[:hostport]))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,43 +0,0 @@
|
||||||
require 'fileutils'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
# will be removed.
|
|
||||||
class CleanMachineFolder
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
clean_machine_folder(env[:vm].driver.read_machine_folder)
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
def clean_machine_folder(machine_folder)
|
|
||||||
folder = File.join(machine_folder, "*")
|
|
||||||
|
|
||||||
# Small safeguard against potentially unwanted rm-rf, since the default
|
|
||||||
# machine folder will typically always be greater than 10 characters long.
|
|
||||||
# For users with it < 10, out of luck?
|
|
||||||
return if folder.length < 10
|
|
||||||
|
|
||||||
Dir[folder].each do |f|
|
|
||||||
next unless File.directory?(f)
|
|
||||||
|
|
||||||
keep = Dir["#{f}/**/*"].find do |d|
|
|
||||||
# Find a file that doesn't have ".xml-prev" as the suffix,
|
|
||||||
# which signals that we want to keep this folder
|
|
||||||
File.file?(d) && !(File.basename(d) =~ /\.vbox-prev$/)
|
|
||||||
end
|
|
||||||
|
|
||||||
FileUtils.rm_rf(f) if !keep
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class ClearForwardedPorts
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting")
|
|
||||||
env[:vm].driver.clear_forwarded_ports
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,31 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class ClearNetworkInterfaces
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
# Create the adapters array to make all adapters nothing.
|
|
||||||
# We do adapters 2 to 8 because that is every built-in adapter
|
|
||||||
# excluding the NAT adapter on port 1 which Vagrant always
|
|
||||||
# expects to exist.
|
|
||||||
adapters = []
|
|
||||||
2.upto(8).each do |i|
|
|
||||||
adapters << {
|
|
||||||
:adapter => i,
|
|
||||||
:type => :none
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# "Enable" all the adapters we setup.
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.clear_network_interfaces.deleting")
|
|
||||||
env[:vm].driver.enable_adapters(adapters)
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class ClearSharedFolders
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
@env = env
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
env[:vm].driver.clear_shared_folders
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,36 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class Customize
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
customizations = env[:vm].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.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
result = env[:vm].driver.execute_command(processed_command)
|
|
||||||
if result.exit_code != 0
|
|
||||||
raise Errors::VMCustomizationFailed, {
|
|
||||||
:command => processed_command.inspect,
|
|
||||||
:error => result.stderr
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,22 +0,0 @@
|
||||||
require 'log4r'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class DefaultName
|
|
||||||
def initialize(app, env)
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::vm::defaultname")
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class Destroy
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")
|
|
||||||
env[:vm].driver.delete
|
|
||||||
env[:vm].uuid = nil
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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,92 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class ForwardPorts
|
|
||||||
def initialize(app,env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
#--------------------------------------------------------------
|
|
||||||
# Execution
|
|
||||||
#--------------------------------------------------------------
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
# Get the ports we're forwarding
|
|
||||||
ports = forward_port_definitions
|
|
||||||
|
|
||||||
# Warn if we're port forwarding to any privileged ports...
|
|
||||||
threshold_check(ports)
|
|
||||||
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
|
|
||||||
forward_ports(ports)
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
# This returns an array of forwarded ports with overrides properly
|
|
||||||
# squashed.
|
|
||||||
def forward_port_definitions
|
|
||||||
# Get all the port mappings in the order they're defined and
|
|
||||||
# organize them by their guestport, taking the "last one wins"
|
|
||||||
# approach.
|
|
||||||
guest_port_mapping = {}
|
|
||||||
@env[:vm].config.vm.forwarded_ports.each do |options|
|
|
||||||
guest_port_mapping[options[:guestport]] = options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the values, since the order doesn't really matter
|
|
||||||
guest_port_mapping.values
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method checks for any forwarded ports on the host below
|
|
||||||
# 1024, which causes the forwarded ports to fail.
|
|
||||||
def threshold_check(ports)
|
|
||||||
ports.each do |options|
|
|
||||||
if options[:hostport] <= 1024
|
|
||||||
@env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def forward_ports(mappings)
|
|
||||||
ports = []
|
|
||||||
|
|
||||||
interfaces = @env[:vm].driver.read_network_interfaces
|
|
||||||
|
|
||||||
mappings.each do |options|
|
|
||||||
message_attributes = {
|
|
||||||
:guest_port => options[:guestport],
|
|
||||||
:host_port => options[:hostport],
|
|
||||||
:adapter => options[:adapter]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assuming the only reason to establish port forwarding is
|
|
||||||
# because the VM is using Virtualbox NAT networking. Host-only
|
|
||||||
# bridged networking don't require port-forwarding and establishing
|
|
||||||
# forwarded ports on these attachment types has uncertain behaviour.
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
|
|
||||||
message_attributes))
|
|
||||||
|
|
||||||
# Port forwarding requires the network interface to be a NAT interface,
|
|
||||||
# so verify that that is the case.
|
|
||||||
if interfaces[options[:adapter]][:type] != :nat
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.non_nat",
|
|
||||||
message_attributes))
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add the options to the ports array to send to the driver later
|
|
||||||
ports << options.merge(:name => options[:name], :adapter => options[:adapter])
|
|
||||||
end
|
|
||||||
|
|
||||||
if !ports.empty?
|
|
||||||
# We only need to forward ports if there are any to forward
|
|
||||||
@env[:vm].driver.forward_ports(ports)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,36 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class Halt
|
|
||||||
def initialize(app, env, options=nil)
|
|
||||||
@app = app
|
|
||||||
env.merge!(options || {})
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
current_state = env[:vm].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"]
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.halt.graceful")
|
|
||||||
env[:vm].guest.halt
|
|
||||||
end
|
|
||||||
|
|
||||||
# If we're not powered off now, then force it
|
|
||||||
if env[:vm].state != :poweroff
|
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.halt.force")
|
|
||||||
env[:vm].driver.halt
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sleep for a second to verify that the VM properly
|
|
||||||
# cleans itself up
|
|
||||||
sleep 1 if !env["vagrant.test"]
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class HostName
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
host_name = env[:vm].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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,53 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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|
|
|
||||||
env[:ui].clear_line
|
|
||||||
env[:ui].report_progress(progress, 100, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Clear the line one last time since the progress meter doesn't disappear
|
|
||||||
# immediately.
|
|
||||||
env[:ui].clear_line
|
|
||||||
|
|
||||||
# If we got interrupted, then the import could have been
|
|
||||||
# interrupted and its not a big deal. Just return out.
|
|
||||||
return if env[:interrupted]
|
|
||||||
|
|
||||||
# Flag as erroneous and return if import failed
|
|
||||||
raise Errors::VMImportFailure if !env[:vm].uuid
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,406 +0,0 @@
|
||||||
require 'set'
|
|
||||||
|
|
||||||
require 'log4r'
|
|
||||||
|
|
||||||
require 'vagrant/util/network_ip'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
|
|
||||||
def initialize(app, env)
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::vm::network")
|
|
||||||
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
# First we have to get the array of adapters that we need
|
|
||||||
# to create on the virtual machine itself, as well as the
|
|
||||||
# driver-agnostic network configurations for each.
|
|
||||||
@logger.debug("Determining adapters and networks...")
|
|
||||||
adapters = []
|
|
||||||
networks = []
|
|
||||||
env[:vm].config.vm.networks.each do |type, args|
|
|
||||||
# Get the normalized configuration we'll use around
|
|
||||||
config = send("#{type}_config", args)
|
|
||||||
|
|
||||||
# Get the virtualbox adapter configuration
|
|
||||||
adapter = send("#{type}_adapter", config)
|
|
||||||
adapters << adapter
|
|
||||||
|
|
||||||
# Get the network configuration
|
|
||||||
network = send("#{type}_network_config", config)
|
|
||||||
network[:_auto_config] = true if config[:auto_config]
|
|
||||||
networks << network
|
|
||||||
end
|
|
||||||
|
|
||||||
if !adapters.empty?
|
|
||||||
# Automatically assign an adapter number to any adapters
|
|
||||||
# that aren't explicitly set.
|
|
||||||
@logger.debug("Assigning adapter locations...")
|
|
||||||
assign_adapter_locations(adapters)
|
|
||||||
|
|
||||||
# Verify that our adapters are good just prior to enabling them.
|
|
||||||
verify_adapters(adapters)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Continue the middleware chain. We're done with our VM
|
|
||||||
# setup until after it is booted.
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
if !adapters.empty? && !networks.empty?
|
|
||||||
# Determine the interface numbers for the guest.
|
|
||||||
assign_interface_numbers(networks, adapters)
|
|
||||||
|
|
||||||
# Configure all the network interfaces on the guest. We only
|
|
||||||
# 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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method assigns the adapter to use for the adapter.
|
|
||||||
# e.g. it says that the first adapter is actually on the
|
|
||||||
# virtual machine's 2nd adapter location.
|
|
||||||
#
|
|
||||||
# It determines the adapter numbers by simply finding the
|
|
||||||
# "next available" in each case.
|
|
||||||
#
|
|
||||||
# The adapters are modified in place by adding an ":adapter"
|
|
||||||
# field to each.
|
|
||||||
def assign_adapter_locations(adapters)
|
|
||||||
available = Set.new(1..8)
|
|
||||||
|
|
||||||
# Determine which NICs are actually available.
|
|
||||||
interfaces = @env[:vm].driver.read_network_interfaces
|
|
||||||
interfaces.each do |number, nic|
|
|
||||||
# Remove the number from the available NICs if the
|
|
||||||
# NIC is in use.
|
|
||||||
available.delete(number) if nic[:type] != :none
|
|
||||||
end
|
|
||||||
|
|
||||||
# Based on the available set, assign in order to
|
|
||||||
# the adapters.
|
|
||||||
available = available.to_a.sort
|
|
||||||
@logger.debug("Available NICs: #{available.inspect}")
|
|
||||||
adapters.each do |adapter|
|
|
||||||
# Ignore the adapters that already have been assigned
|
|
||||||
if !adapter[:adapter]
|
|
||||||
# If we have no available adapters, then that is an exceptional
|
|
||||||
# event.
|
|
||||||
raise Errors::NetworkNoAdapters if available.empty?
|
|
||||||
|
|
||||||
# Otherwise, assign as the adapter the next available item
|
|
||||||
adapter[:adapter] = available.shift
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verifies that the adapter configurations look good. This will
|
|
||||||
# raise an exception in the case that any errors occur.
|
|
||||||
def verify_adapters(adapters)
|
|
||||||
# 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])
|
|
||||||
used.add(adapter[:adapter])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Assigns the actual interface number of a network based on the
|
|
||||||
# enabled NICs on the virtual machine.
|
|
||||||
#
|
|
||||||
# This interface number is used by the guest to configure the
|
|
||||||
# NIC on the guest VM.
|
|
||||||
#
|
|
||||||
# The networks are modified in place by adding an ":interface"
|
|
||||||
# field to each.
|
|
||||||
def assign_interface_numbers(networks, adapters)
|
|
||||||
current = 0
|
|
||||||
adapter_to_interface = {}
|
|
||||||
|
|
||||||
# Make a first pass to assign interface numbers by adapter location
|
|
||||||
vm_adapters = @env[:vm].driver.read_network_interfaces
|
|
||||||
vm_adapters.sort.each do |number, adapter|
|
|
||||||
if adapter[:type] != :none
|
|
||||||
# Not used, so assign the interface number and increment
|
|
||||||
adapter_to_interface[number] = current
|
|
||||||
current += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Make a pass through the adapters to assign the :interface
|
|
||||||
# key to each network configuration.
|
|
||||||
adapters.each_index do |i|
|
|
||||||
adapter = adapters[i]
|
|
||||||
network = networks[i]
|
|
||||||
|
|
||||||
# Figure out the interface number by simple lookup
|
|
||||||
network[:interface] = adapter_to_interface[adapter[:adapter]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def hostonly_config(args)
|
|
||||||
ip = args[0]
|
|
||||||
options = args[1] || {}
|
|
||||||
|
|
||||||
# Determine if we're dealing with a static IP or a DHCP-served IP.
|
|
||||||
type = ip == :dhcp ? :dhcp : :static
|
|
||||||
|
|
||||||
# Default IP is in the 20-bit private network block for DHCP based networks
|
|
||||||
ip = "172.28.128.1" if type == :dhcp
|
|
||||||
|
|
||||||
options = {
|
|
||||||
:type => type,
|
|
||||||
:ip => ip,
|
|
||||||
:netmask => "255.255.255.0",
|
|
||||||
:adapter => nil,
|
|
||||||
:mac => nil,
|
|
||||||
:name => nil,
|
|
||||||
:auto_config => true
|
|
||||||
}.merge(options)
|
|
||||||
|
|
||||||
# Verify that this hostonly network wouldn't conflict with any
|
|
||||||
# bridged interfaces
|
|
||||||
verify_no_bridge_collision(options)
|
|
||||||
|
|
||||||
# Get the network address and IP parts which are used for many
|
|
||||||
# default calculations
|
|
||||||
netaddr = network_address(options[:ip], options[:netmask])
|
|
||||||
ip_parts = netaddr.split(".").map { |i| i.to_i }
|
|
||||||
|
|
||||||
# Calculate the adapter IP, which we assume is the IP ".1" at the
|
|
||||||
# end usually.
|
|
||||||
adapter_ip = ip_parts.dup
|
|
||||||
adapter_ip[3] += 1
|
|
||||||
options[:adapter_ip] ||= adapter_ip.join(".")
|
|
||||||
|
|
||||||
if type == :dhcp
|
|
||||||
# Calculate the DHCP server IP, which is the network address
|
|
||||||
# with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2"
|
|
||||||
dhcp_ip = ip_parts.dup
|
|
||||||
dhcp_ip[3] += 2
|
|
||||||
options[:dhcp_ip] ||= dhcp_ip.join(".")
|
|
||||||
|
|
||||||
# Calculate the lower and upper bound for the DHCP server
|
|
||||||
dhcp_lower = ip_parts.dup
|
|
||||||
dhcp_lower[3] += 3
|
|
||||||
options[:dhcp_lower] ||= dhcp_lower.join(".")
|
|
||||||
|
|
||||||
dhcp_upper = ip_parts.dup
|
|
||||||
dhcp_upper[3] = 254
|
|
||||||
options[:dhcp_upper] ||= dhcp_upper.join(".")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the hostonly network configuration
|
|
||||||
return options
|
|
||||||
end
|
|
||||||
|
|
||||||
def hostonly_adapter(config)
|
|
||||||
@logger.debug("Searching for matching network: #{config[:ip]}")
|
|
||||||
interface = find_matching_hostonly_network(config)
|
|
||||||
|
|
||||||
if !interface
|
|
||||||
@logger.debug("Network not found. Creating if we can.")
|
|
||||||
|
|
||||||
# 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]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Otherwise, we create a new network and put the net network
|
|
||||||
# in the list of available networks so other network definitions
|
|
||||||
# can use it!
|
|
||||||
interface = create_hostonly_network(config)
|
|
||||||
@logger.debug("Created network: #{interface[:name]}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if config[:type] == :dhcp
|
|
||||||
# Check that if there is a DHCP server attached on our interface,
|
|
||||||
# then it is identical. Otherwise, we can't set it.
|
|
||||||
if interface[:dhcp]
|
|
||||||
valid = interface[:dhcp][:ip] == config[:dhcp_ip] &&
|
|
||||||
interface[:dhcp][:lower] == config[:dhcp_lower] &&
|
|
||||||
interface[:dhcp][:upper] == config[:dhcp_upper]
|
|
||||||
|
|
||||||
raise 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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
:adapter => config[:adapter],
|
|
||||||
:type => :hostonly,
|
|
||||||
:hostonly => interface[:name],
|
|
||||||
:mac_address => config[:mac],
|
|
||||||
:nic_type => config[:nic_type]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def hostonly_network_config(config)
|
|
||||||
return {
|
|
||||||
:type => config[:type],
|
|
||||||
:adapter_ip => config[:adapter_ip],
|
|
||||||
:ip => config[:ip],
|
|
||||||
:netmask => config[:netmask]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new hostonly network that matches the network requested
|
|
||||||
# by the given host-only network configuration.
|
|
||||||
def create_hostonly_network(config)
|
|
||||||
# Create the options that are going to be used to create our
|
|
||||||
# new network.
|
|
||||||
options = config.dup
|
|
||||||
options[:ip] = options[:adapter_ip]
|
|
||||||
|
|
||||||
@env[:vm].driver.create_host_only_network(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Finds a host only network that matches our configuration on VirtualBox.
|
|
||||||
# This will return nil if a matching network does not exist.
|
|
||||||
def find_matching_hostonly_network(config)
|
|
||||||
this_netaddr = network_address(config[:ip], config[:netmask])
|
|
||||||
|
|
||||||
@env[:vm].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])
|
|
||||||
return interface
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verifies that a host-only network subnet would not collide with
|
|
||||||
# a bridged networking interface.
|
|
||||||
#
|
|
||||||
# If the subnets overlap in any way then the host only network
|
|
||||||
# will not work because the routing tables will force the traffic
|
|
||||||
# onto the real interface rather than the virtualbox interface.
|
|
||||||
def verify_no_bridge_collision(options)
|
|
||||||
this_netaddr = network_address(options[:ip], options[:netmask])
|
|
||||||
|
|
||||||
@env[:vm].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"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def bridged_config(args)
|
|
||||||
options = args[0] || {}
|
|
||||||
options = {} if !options.is_a?(Hash)
|
|
||||||
|
|
||||||
return {
|
|
||||||
:adapter => nil,
|
|
||||||
:mac => nil,
|
|
||||||
:bridge => nil,
|
|
||||||
:auto_config => true,
|
|
||||||
:use_dhcp_assigned_default_route => false
|
|
||||||
}.merge(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def bridged_adapter(config)
|
|
||||||
# Find the bridged interfaces that are available
|
|
||||||
bridgedifs = @env[:vm].driver.read_bridged_interfaces
|
|
||||||
bridgedifs.delete_if { |interface| interface[:status] == "Down" }
|
|
||||||
|
|
||||||
# The name of the chosen bridge interface will be assigned to this
|
|
||||||
# variable.
|
|
||||||
chosen_bridge = nil
|
|
||||||
|
|
||||||
if config[:bridge]
|
|
||||||
@logger.debug("Bridge was directly specified in config, searching for: #{config[:bridge]}")
|
|
||||||
|
|
||||||
# Search for a matching bridged interface
|
|
||||||
bridgedifs.each do |interface|
|
|
||||||
if interface[:name].downcase == config[:bridge].downcase
|
|
||||||
@logger.debug("Specific bridge found as configured in the Vagrantfile. Using it.")
|
|
||||||
chosen_bridge = interface[:name]
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# If one wasn't found, then we notify the user here.
|
|
||||||
if !chosen_bridge
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.bridged_networking.specific_not_found",
|
|
||||||
:bridge => config[:bridge])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# If we still don't have a bridge chosen (this means that one wasn't
|
|
||||||
# specified in the Vagrantfile, or the bridge specified in the Vagrantfile
|
|
||||||
# wasn't found), then we fall back to the normal means of searchign for a
|
|
||||||
# bridged network.
|
|
||||||
if !chosen_bridge
|
|
||||||
if bridgedifs.length == 1
|
|
||||||
# One bridgable interface? Just use it.
|
|
||||||
chosen_bridge = bridgedifs[0][:name]
|
|
||||||
@logger.debug("Only one bridged interface available. Using it by default.")
|
|
||||||
else
|
|
||||||
# More than one bridgable interface requires a user decision, so
|
|
||||||
# show options to choose from.
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.bridged_networking.available",
|
|
||||||
:prefix => false)
|
|
||||||
bridgedifs.each_index do |index|
|
|
||||||
interface = bridgedifs[index]
|
|
||||||
@env[:ui].info("#{index + 1}) #{interface[:name]}", :prefix => false)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The range of valid choices
|
|
||||||
valid = Range.new(1, bridgedifs.length)
|
|
||||||
|
|
||||||
# The choice that the user has chosen as the bridging interface
|
|
||||||
choice = nil
|
|
||||||
while !valid.include?(choice)
|
|
||||||
choice = @env[:ui].ask("What interface should the network bridge to? ")
|
|
||||||
choice = choice.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
chosen_bridge = bridgedifs[choice - 1][:name]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@logger.info("Bridging adapter #{config[:adapter]} to #{chosen_bridge}")
|
|
||||||
|
|
||||||
# Given the choice we can now define the adapter we're using
|
|
||||||
return {
|
|
||||||
:adapter => config[:adapter],
|
|
||||||
:type => :bridged,
|
|
||||||
:bridge => chosen_bridge,
|
|
||||||
:mac_address => config[:mac],
|
|
||||||
:nic_type => config[:nic_type]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def bridged_network_config(config)
|
|
||||||
return {
|
|
||||||
:type => :dhcp,
|
|
||||||
:use_dhcp_assigned_default_route => config[:use_dhcp_assigned_default_route]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,196 +0,0 @@
|
||||||
require 'digest/md5'
|
|
||||||
require 'fileutils'
|
|
||||||
require 'pathname'
|
|
||||||
|
|
||||||
require 'log4r'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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")
|
|
||||||
@app = app
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
verify_settings if nfs_enabled?
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
extract_folders
|
|
||||||
|
|
||||||
if !folders.empty?
|
|
||||||
prepare_folders
|
|
||||||
export_folders
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
mount_folders if !folders.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the folders which are to be synced via NFS.
|
|
||||||
def folders
|
|
||||||
@folders ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Removes the NFS enabled shared folders from the configuration,
|
|
||||||
# so they will no longer be mounted by the actual shared folder
|
|
||||||
# task.
|
|
||||||
def extract_folders
|
|
||||||
# Load the NFS enabled shared folders
|
|
||||||
@folders = {}
|
|
||||||
@env[:vm].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.
|
|
||||||
folder = opts.dup
|
|
||||||
hostpath = Pathname.new(opts[:hostpath]).expand_path(@env[:root_path])
|
|
||||||
|
|
||||||
if !hostpath.directory? && opts[:create]
|
|
||||||
# Host path doesn't exist, so let's create it.
|
|
||||||
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
|
|
||||||
|
|
||||||
begin
|
|
||||||
FileUtils.mkpath(hostpath)
|
|
||||||
rescue Errno::EACCES
|
|
||||||
raise Errors::SharedFolderCreateFailed, :path => hostpath.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the hostpath now that it exists.
|
|
||||||
folder[:hostpath] = hostpath.to_s
|
|
||||||
|
|
||||||
# Assign the folder to our instance variable for later use
|
|
||||||
@folders[key] = folder
|
|
||||||
|
|
||||||
# Disable the folder so that regular shared folders don't try to
|
|
||||||
# mount it.
|
|
||||||
opts[:disabled] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepares the settings for the NFS folders, such as setting the
|
|
||||||
# options on the NFS folders.
|
|
||||||
def prepare_folders
|
|
||||||
@folders = @folders.inject({}) do |acc, data|
|
|
||||||
key, opts = data
|
|
||||||
opts[:map_uid] = prepare_permission(:uid, opts)
|
|
||||||
opts[:map_gid] = prepare_permission(:gid, opts)
|
|
||||||
opts[:nfs_version] ||= 3
|
|
||||||
|
|
||||||
# The poor man's UUID. An MD5 hash here is sufficient since
|
|
||||||
# we need a 32 character "uuid" to represent the filesystem
|
|
||||||
# of an export. Hashing the host path is safe because two of
|
|
||||||
# the same host path will hash to the same fsid.
|
|
||||||
opts[:uuid] = Digest::MD5.hexdigest(opts[:hostpath])
|
|
||||||
|
|
||||||
acc[key] = opts
|
|
||||||
acc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepares the UID/GID settings for a single folder.
|
|
||||||
def prepare_permission(perm, opts)
|
|
||||||
key = "map_#{perm}".to_sym
|
|
||||||
return nil if opts.has_key?(key) && opts[key].nil?
|
|
||||||
|
|
||||||
# The options on the hash get priority, then the default
|
|
||||||
# values
|
|
||||||
value = opts.has_key?(key) ? opts[key] : @env[:vm].config.nfs.send(key)
|
|
||||||
return value if value != :auto
|
|
||||||
|
|
||||||
# Get UID/GID from folder if we've made it this far
|
|
||||||
# (value == :auto)
|
|
||||||
stat = File.stat(opts[:hostpath])
|
|
||||||
return stat.send(perm)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Uses the host class to export the folders via NFS. This typically
|
|
||||||
# involves adding a line to `/etc/exports` for this VM, but it is
|
|
||||||
# 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)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Uses the system class to mount the NFS folders.
|
|
||||||
def mount_folders
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.nfs.mounting")
|
|
||||||
|
|
||||||
# Only mount the folders which have a guest path specified
|
|
||||||
mount_folders = {}
|
|
||||||
folders.each do |name, opts|
|
|
||||||
if opts[:guestpath]
|
|
||||||
mount_folders[name] = opts.dup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@env[:vm].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|
|
|
||||||
if opts[:type] == :hostonly
|
|
||||||
@env[:vm].driver.read_host_only_interfaces.each do |interface|
|
|
||||||
if interface[:name] == opts[:hostonly]
|
|
||||||
return interface[:ip]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the IP address of the guest by looking at the first
|
|
||||||
# enabled host only network.
|
|
||||||
#
|
|
||||||
# @return [String]
|
|
||||||
def guest_ip
|
|
||||||
@env[:vm].config.vm.networks.each do |type, args|
|
|
||||||
if type == :hostonly && args[0].is_a?(String)
|
|
||||||
return args[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if there are any NFS enabled shared folders.
|
|
||||||
def nfs_enabled?
|
|
||||||
@env[:vm].config.vm.shared_folders.each do |key, opts|
|
|
||||||
return true if opts[:nfs]
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,61 +0,0 @@
|
||||||
require "log4r"
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class Provision
|
|
||||||
def initialize(app, env)
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::vm::provision")
|
|
||||||
@app = app
|
|
||||||
|
|
||||||
env["provision.enabled"] = true if !env.has_key?("provision.enabled")
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
provisioners = nil
|
|
||||||
|
|
||||||
# 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
|
|
||||||
provisioners.map { |p| p.prepare }
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
if enabled
|
|
||||||
# Take prepared provisioners and run the provisioning
|
|
||||||
provisioners.each do |instance|
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.provision.beginning",
|
|
||||||
:provisioner => instance.class)
|
|
||||||
instance.provision!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def enabled_provisioners
|
|
||||||
enabled = []
|
|
||||||
@env[:vm].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
|
|
||||||
# specified.
|
|
||||||
if !@env["provision.types"].include?(provisioner.shortcut.to_s)
|
|
||||||
@logger.debug("Skipping provisioner: #{provisioner.shortcut}")
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
enabled << provisioner.provisioner.new(@env, provisioner.config)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the enable provisioners
|
|
||||||
enabled
|
|
||||||
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,20 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class PruneNFSExports
|
|
||||||
def initialize(app, env)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
if env[:host]
|
|
||||||
valid_ids = env[:vm].driver.read_vms
|
|
||||||
env[:host].nfs_prune(valid_ids)
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,78 +0,0 @@
|
||||||
require "log4r"
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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")
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
# Set the env on an instance variable so we can access it in
|
|
||||||
# helpers.
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
# Enable the host IO cache on the sata controller. Note that
|
|
||||||
# if this fails then its not a big deal, so we don't raise any
|
|
||||||
# errors. The Host IO cache vastly improves disk IO performance
|
|
||||||
# for VMs.
|
|
||||||
command = [
|
|
||||||
"storagectl", env[:vm].uuid,
|
|
||||||
"--name", "SATA Controller",
|
|
||||||
"--hostiocache", "on"
|
|
||||||
]
|
|
||||||
attempt_and_log(command, "Enabling the Host I/O cache on the SATA controller...")
|
|
||||||
|
|
||||||
enable_dns_proxy = true
|
|
||||||
begin
|
|
||||||
contents = File.read("/etc/resolv.conf")
|
|
||||||
|
|
||||||
if contents =~ /^nameserver 127\.0\.0\.1$/
|
|
||||||
# The use of both natdnsproxy and natdnshostresolver break on
|
|
||||||
# Ubuntu 12.04 that uses resolvconf with localhost. When used
|
|
||||||
# VirtualBox will give the client dns server 10.0.2.3, while
|
|
||||||
# not binding to that address itself. Therefore disable this
|
|
||||||
# feature if host uses the resolvconf server 127.0.0.1
|
|
||||||
@logger.info("Disabling DNS proxy since resolv.conf contains 127.0.0.1")
|
|
||||||
enable_dns_proxy = false
|
|
||||||
end
|
|
||||||
rescue Errno::ENOENT; end
|
|
||||||
|
|
||||||
# Enable/disable the NAT DNS proxy as necessary
|
|
||||||
if enable_dns_proxy
|
|
||||||
command = [
|
|
||||||
"modifyvm", env[:vm].uuid,
|
|
||||||
"--natdnsproxy1", "on"
|
|
||||||
]
|
|
||||||
attempt_and_log(command, "Enable the NAT DNS proxy on adapter 1...")
|
|
||||||
else
|
|
||||||
command = [ "modifyvm", env[:vm].uuid, "--natdnsproxy1", "off" ]
|
|
||||||
attempt_and_log(command, "Disable the NAT DNS proxy on adapter 1...")
|
|
||||||
command = [ "modifyvm", env[:vm].uuid, "--natdnshostresolver1", "off" ]
|
|
||||||
attempt_and_log(command, "Disable the NAT DNS resolver on adapter 1...")
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# This is just a helper method that executes a single command, logs
|
|
||||||
# the given string to the log, and also includes the exit status in
|
|
||||||
# the log message.
|
|
||||||
#
|
|
||||||
# @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)
|
|
||||||
@logger.info("#{log} (exit status = #{result.exit_code})")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,54 +0,0 @@
|
||||||
require 'pathname'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
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
|
|
||||||
|
|
||||||
env["package.include"] ||= []
|
|
||||||
env["package.vagrantfile"] ||= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
files = {}
|
|
||||||
env["package.include"].each do |file|
|
|
||||||
source = Pathname.new(file)
|
|
||||||
dest = nil
|
|
||||||
|
|
||||||
# If the source is relative then we add the file as-is to the include
|
|
||||||
# directory. Otherwise, we copy only the file into the root of the
|
|
||||||
# include directory. Kind of strange, but seems to match what people
|
|
||||||
# expect based on history.
|
|
||||||
if source.relative?
|
|
||||||
dest = source
|
|
||||||
else
|
|
||||||
dest = source.basename
|
|
||||||
end
|
|
||||||
|
|
||||||
# Assign the mapping
|
|
||||||
files[file] = dest
|
|
||||||
end
|
|
||||||
|
|
||||||
if env["package.vagrantfile"]
|
|
||||||
# Vagrantfiles are treated special and mapped to a specific file
|
|
||||||
files[env["package.vagrantfile"]] = "_Vagrantfile"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verify the mapping
|
|
||||||
files.each do |from, _|
|
|
||||||
raise Errors::PackageIncludeMissing, :file => from if !File.exist?(from)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Save the mapping
|
|
||||||
env["package.files"] = files
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,114 +0,0 @@
|
||||||
require 'pathname'
|
|
||||||
|
|
||||||
require 'log4r'
|
|
||||||
|
|
||||||
module Vagrant
|
|
||||||
module Action
|
|
||||||
module VM
|
|
||||||
class ShareFolders
|
|
||||||
def initialize(app, env)
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action::vm::share_folders")
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
@env = env
|
|
||||||
|
|
||||||
prepare_folders
|
|
||||||
create_metadata
|
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
|
|
||||||
mount_shared_folders
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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|
|
|
||||||
key, value = data
|
|
||||||
|
|
||||||
next acc if value[:disabled]
|
|
||||||
|
|
||||||
# This to prevent overwriting the actual shared folders data
|
|
||||||
value = value.dup
|
|
||||||
acc[key] = value
|
|
||||||
acc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepares the shared folders by verifying they exist and creating them
|
|
||||||
# if they don't.
|
|
||||||
def prepare_folders
|
|
||||||
shared_folders.each do |name, options|
|
|
||||||
hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
|
|
||||||
|
|
||||||
if !hostpath.directory? && options[:create]
|
|
||||||
# Host path doesn't exist, so let's create it.
|
|
||||||
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
|
|
||||||
|
|
||||||
begin
|
|
||||||
hostpath.mkpath
|
|
||||||
rescue Errno::EACCES
|
|
||||||
raise Errors::SharedFolderCreateFailed, :path => hostpath.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_metadata
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.share_folders.creating")
|
|
||||||
|
|
||||||
folders = []
|
|
||||||
shared_folders.each do |name, data|
|
|
||||||
folders << {
|
|
||||||
:name => name,
|
|
||||||
:hostpath => File.expand_path(data[:hostpath], @env[:root_path]),
|
|
||||||
:transient => data[:transient]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
@env[:vm].driver.share_folders(folders)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mount_shared_folders
|
|
||||||
@env[:ui].info I18n.t("vagrant.actions.vm.share_folders.mounting")
|
|
||||||
|
|
||||||
# short guestpaths first, so we don't step on ourselves
|
|
||||||
folders = shared_folders.sort_by do |name, data|
|
|
||||||
if data[:guestpath]
|
|
||||||
data[:guestpath].length
|
|
||||||
else
|
|
||||||
# A long enough path to just do this at the end.
|
|
||||||
10000
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Go through each folder and mount
|
|
||||||
folders.each do |name, data|
|
|
||||||
if data[:guestpath]
|
|
||||||
# Guest path specified, so mount the folder to specified point
|
|
||||||
@env[:ui].info(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
|
|
||||||
:name => name,
|
|
||||||
:guest_path => data[:guestpath]))
|
|
||||||
|
|
||||||
# Dup the data so we can pass it to the guest API
|
|
||||||
data = data.dup
|
|
||||||
|
|
||||||
# Calculate the owner and group
|
|
||||||
data[:owner] ||= @env[:vm].config.ssh.username
|
|
||||||
data[:group] ||= @env[:vm].config.ssh.username
|
|
||||||
|
|
||||||
# Mount the actual folder
|
|
||||||
@env[:vm].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",
|
|
||||||
:name => name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue