Skip to content

Commit

Permalink
Add support for managing host /etc/hosts file.
Browse files Browse the repository at this point in the history
This commit also include preservation original /etc/hosts file on
guest machines.
  • Loading branch information
smdahlen committed Jun 18, 2013
1 parent 2a190a2 commit a39877a
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 210 deletions.
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
source 'https://rubygems.org'

gem 'vagrant', :git => 'git:https://github.com/mitchellh/vagrant.git', :tag => 'v1.2.1'
group :development do
gem 'vagrant', :git => 'git:https://github.com/mitchellh/vagrant.git', :tag => 'v1.2.2'
end

gemspec
33 changes: 0 additions & 33 deletions lib/vagrant-hostmanager/action/delete_guests.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,32 @@
module VagrantPlugins
module HostManager
module Action
class UpdateGuests
class UpdateHostsFile
include HostsFile

def initialize(app, env)
@app = app
@machine = env[:machine]
@logger = Log4r::Logger.new('vagrant::hostmanager::update_guests')
@logger = Log4r::Logger.new('vagrant::hostmanager::update_hosts_file')
end

def call(env)
# check if machine is already active
#return @app.call(env) if @machine.id
return @app.call(env) if @machine.id

# check config to see if the hosts file should be update automatically
return @app.call(env) unless @machine.config.hostmanager.enabled?
@logger.info 'Updating /etc/hosts file automatically'

# continue the action stack so the machine will be created
@app.call(env)

# update /etc/hosts file on each active machine
update_guests(@machine,@machine.provider_name)
# update /etc/hosts file on active machines
update_guests(@machine.env, @machine.provider_name)

# update /etc/hosts files on host if enabled
if @machine.config.hostmanager.manage_host?
update_host(@machine.env, @machine.provider_name)
end
end
end
end
Expand Down
11 changes: 5 additions & 6 deletions lib/vagrant-hostmanager/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Command < Vagrant.plugin('2', :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = 'Usage: vagrant hostmanager [vm-name]'
o.banner = 'Usage: vagrant hostmanager'
o.separator ''
o.version = VagrantPlugins::HostManager::VERSION
o.program_name = 'vagrant hostmanager'
Expand All @@ -17,14 +17,13 @@ def execute
end
end

argv = parse_options(opts)
return if !argv
parse_options(opts)

options[:provider] ||= @env.default_provider

with_target_vms(argv, options) do |machine|
update_guests(machine, machine.provider_name)
update_local(machine)
update_guests(@env, options[:provider])
if (@env.config_global.hostmanager.manage_host?)
update_host(@env, options[:provider])
end
end
end
Expand Down
11 changes: 6 additions & 5 deletions lib/vagrant-hostmanager/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ module VagrantPlugins
module HostManager
class Config < Vagrant.plugin('2', :config)
attr_accessor :enabled
attr_accessor :manage_local
attr_accessor :manage_host
attr_accessor :ignore_private_ip
attr_accessor :aliases
attr_accessor :include_offline

alias_method :enabled?, :enabled
alias_method :include_offline?, :include_offline
alias_method :manage_host?, :manage_host

def initialize
@enabled = false
@manage_local = true
@manage_local = false
@ignore_private_ip = UNSET_VALUE
@aliases = Array.new
@include_offline = false
Expand All @@ -33,9 +34,9 @@ def validate(machine)
errors << validate_bool('hostmanager.include_offline', include_offline)

# check if manage_local option is either true or false
if ![TrueClass, FalseClass].include?(manage_local.class)
errors << "A value for hostmanager.manage_local can be true or false."
end
# if ![TrueClass, FalseClass].include?(manage_local.class)
# errors << "A value for hostmanager.manage_local can be true or false."
# end

# check if ignore_private_ip option is either true or false (or UNSET_VALUE)
if @ignore_private_ip != UNSET_VALUE
Expand Down
194 changes: 52 additions & 142 deletions lib/vagrant-hostmanager/hosts_file.rb
Original file line number Diff line number Diff line change
@@ -1,175 +1,85 @@
require 'tempfile'

module VagrantPlugins
module HostManager
module HostsFile
def update_guests(machine, provider)
machines = []

env = machine.env
# create the temporary hosts file
path = env.tmp_path

#fetch hosts file from each machine
#for each machine, ensure all machine entries are updated
# add a hosts entry for each active machine matching the provider
def update_guests(env, provider)
entries = get_entries(env, provider)
env.active_machines.each do |name, p|
if provider == p
machines << machine = env.machine(name, provider)
machine.communicate.download('/etc/hosts',path.join("hosts.#{name}"))
target = env.machine(name, p)
next unless target.communicate.ready?

file = env.tmp_path.join("hosts.#{name}")
target.communicate.download('/etc/hosts', file)
update_file(file, entries, env.tmp_path)
target.communicate.upload(file, '/tmp/hosts')
target.communicate.sudo('mv /tmp/hosts /etc/hosts')
FileUtils.rm(file)
end
end
env.active_machines.each do |name, p|
if provider == p
machines.each do |m|
@logger.info "Adding entry for #{m.name} to hosts.#{name}"
update_entry(m,path.join("hosts.#{name}"))
end
end
env.machine(name,p).communicate.upload(path.join("hosts.#{name}"), '/tmp/hosts')
env.machine(name,p).communicate.sudo("mv /tmp/hosts /etc/hosts")
FileUtils.rm(path.join("hosts.#{name}"))
end

end

# delete victim machine from all guests
def delete_guests(victim, provider)
machines = []
def update_host(env, provider)
entries = get_entries(env, provider)
file = env.tmp_path.join('hosts.local')
FileUtils.cp('/etc/hosts', file)
update_file(file, entries, env.tmp_path)
`sudo cp #{file} /etc/hosts`
end

env = victim.env
# create the temporary hosts file
path = env.tmp_path
private

#fetch hosts file from each machine
#for each machine, ensure all machine entries are updated
# add a hosts entry for each active machine matching the provider
env.active_machines.each do |name, p|
if provider == p
machines << machine = env.machine(name, provider)
machine.communicate.download('/etc/hosts',path.join("hosts.#{name}"))
delete_entry(victim,path.join("hosts.#{name}"))
if machine.communicate.ready?
machine.env.ui.info I18n.t('vagrant_hostmanager.action.update_guest', {
:name => machine.name
})
machine.communicate.upload(path.join("hosts.#{name}"), '/tmp/hosts')
machine.communicate.sudo("mv /tmp/hosts /etc/hosts")
end
FileUtils.rm(path.join("hosts.#{name}"))
def update_file(file, entries, tmp_path)
tmp_file = Tempfile.open('hostmanager', tmp_path, 'a')
begin
File.open(file).each_line do |line|
tmp_file << line unless line =~ /# VAGRANT ID:/
end
entries.each { |entry| tmp_file << entry }
ensure
tmp_file.close
FileUtils.cp(tmp_file, file)
tmp_file.unlink
end
end


# define a lambda for looking up a machine's ip address
def get_ip_address(machine)
ip = nil
if machine.config.hostmanager.ignore_private_ip != true
machine.config.vm.networks.each do |network|
key, options = network[0], network[1]
ip = options[:ip] if key == :private_network
next if ip
end
def get_entries(env, provider)
entries = []
get_machines(env, provider).each do |name, p|
if provider == p
machine = env.machine(name, p)
host = machine.config.vm.hostname || name
id = machine.id
ip = get_ip_address(machine)
aliases = machine.config.hostmanager.aliases.join(' ').chomp
entries << "#{ip}\t#{host} #{aliases}\t# VAGRANT ID: #{id}\n"
end
ip || (machine.ssh_info ? machine.ssh_info[:host] : nil)
end

def update_entry(machine,file_name,sudo=false)
delete_entry(machine,file_name,sudo)

host = machine.config.vm.hostname || name
id = machine.id
ip = get_ip_address(machine)
host_aliases = machine.config.hostmanager.aliases.join("\s").chomp
host_entry = "#{ip}\t#{host}\s#{host_aliases}\s# VAGRANT: #{id}\n"
@logger.info "Adding /etc/hosts entry: #{host_entry}"
temp_file_name = Dir::Tmpname.make_tmpname(File.join(machine.env.tmp_path,'hostmanager'), nil)
FileUtils.cp(file_name, temp_file_name)
File.open(temp_file_name,'a') do |tempfile|
@logger.info "writing #{host_entry} to #{tempfile.path}"
tempfile << host_entry
end

if sudo == false
@logger.info "copy #{temp_file_name} #{file_name}"
FileUtils.cp(temp_file_name,file_name)
else
machine.env.ui.info I18n.t('vagrant_hostmanager.action.run_sudo')
@logger.warn "Running sudo to replace local hosts file, enter your local password if prompted..."
@logger.info `sudo cp -v #{temp_file_name} #{file_name}`
end
FileUtils.rm(temp_file_name)
entries
end

def delete_entry(machine,file_name,sudo=false)
host = machine.config.vm.hostname || name
temp_file_name = Dir::Tmpname.make_tmpname(File.join(machine.env.tmp_path,'hostmanager'), nil)
if not machine.id.nil?
tempfile = File.open(temp_file_name,'w') do |f|
File.open(file_name,'r').each_line do |line|
if line.match(/#{machine.id}$/).nil?
f << line
else
@logger.info "Matched #{machine.id}"
end
end
end
if sudo == false
@logger.info "copy #{temp_file_name} #{file_name}"
FileUtils.cp(temp_file_name,file_name)
else
machine.env.ui.info I18n.t('vagrant_hostmanager.action.run_sudo')
@logger.info `sudo cp -v #{temp_file_name} #{file_name}`
end
FileUtils.rm(temp_file_name)
else
@logger.warn "Machine id to delete was empty, skipping..."
def get_ip_address(machine)
ip = nil
if machine.config.hostmanager.ignore_private_ip != true
machine.config.vm.networks.each do |network|
key, options = network[0], network[1]
ip = options[:ip] if key == :private_network
next if ip
end
end

def update_local(machine)
return if machine.id.nil?
update_entry(machine,'/etc/hosts',true)
end

def delete_local(machine)
return if machine.id.nil?
delete_entry(machine,'/etc/hosts',true)
end

def publish_local(tempfile)
@logger.info `sudo cp -v #{tempfile} /etc/hosts`
end


# Copy the temporary hosts file to the specified machine overwritting
# the existing /etc/hosts file.
def update(machine)
path = machine.env.tmp_path.join('hosts')
if machine.communicate.ready?
machine.env.ui.info I18n.t('vagrant_hostmanager.action.update_guest', {
:name => machine.name
})
machine.communicate.download(path, '/etc/hosts')
machine.communicate.upload(path, '/tmp/hosts')
machine.communicate.sudo("mv /tmp/hosts /etc/hosts")
end
ip || (machine.ssh_info ? machine.ssh_info[:host] : nil)
end

private
# Either use the active machines, or loop over all available machines and
# get those with the same provider (aka, ignore boxes that throw MachineNotFound errors).
#
# Returns an array with the same structure as env.active_machines:
# [ [:machine, :virtualbox], [:foo, :virtualbox] ]
def get_machines(env, provider)
if env.config_global.hostmanager.include_offline?
machines = []
env.machine_names.each do |name|
begin
m = env.machine(name, provider)
env.machine(name, provider)
machines << [name, provider]
rescue Vagrant::Errors::MachineNotFound => ex
# ignore this box.
rescue Vagrant::Errors::MachineNotFound
end
end
machines
Expand Down
Loading

0 comments on commit a39877a

Please sign in to comment.