- Changed way how to get IP address of new VM - checks

/var/lib/libvirt/dnsmasq/*.leases files.

- IP address is saved in $data_dir/ip file for future.
  This IP is then checked if it match with VMs MAC address.

- Added ssh, provision and resume subcommand
This commit is contained in:
pradels 2013-04-01 02:17:05 +02:00
parent 1069816a75
commit 99d649a2c3
11 changed files with 201 additions and 54 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env rake
require 'rubygems'
require 'bundler/setup'
#require 'bundler/gem_tasks'
#require 'rubygems'
#require 'bundler/setup'
require 'bundler/gem_tasks'
Bundler::GemHelper.install_tasks

View File

@ -29,12 +29,12 @@ module VagrantPlugins
b2.use CreateDomainVolume
b2.use CreateDomain
b2.use CreateNetworkInterfaces
end
b.use TimedProvision
b.use StartDomain
b.use WaitTillUp
b.use SyncFolders
b2.use TimedProvision
b2.use StartDomain
b2.use WaitTillUp
b2.use SyncFolders
end
end
end
@ -55,6 +55,71 @@ module VagrantPlugins
end
end
# This action is called to SSH into the machine.
def self.action_ssh
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsCreated do |env, b2|
if !env[:result]
b2.use MessageNotCreated
next
end
b2.use SSHExec
end
end
end
# This action is called when `vagrant provision` is called.
def self.action_provision
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsCreated do |env, b2|
if !env[:result]
b2.use MessageNotCreated
next
end
b2.use Provision
b2.use SyncFolders
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 ConfigValidate
b.use Call, IsCreated do |env, b2|
if !env[:result]
b2.use MessageNotCreated
next
end
b2.use ConnectLibvirt
b2.use SuspendDomain
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 ConfigValidate
b.use Call, IsCreated do |env, b2|
if !env[:result]
b2.use MessageNotCreated
next
end
b2.use ConnectLibvirt
b2.use ResumeDomain
end
end
end
# This action is called to read the state of the machine. The resulting
# state is expected to be put into the `:machine_state_id` key.
def self.action_read_state
@ -76,18 +141,7 @@ module VagrantPlugins
end
end
# suspend
# save vm to file
def self.action_suspend
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use Suspend
end
end
action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :Suspend, action_root.join("suspend")
autoload :ConnectLibvirt, action_root.join("connect_libvirt")
autoload :IsCreated, action_root.join("is_created")
autoload :MessageAlreadyCreated, action_root.join("message_already_created")
@ -100,6 +154,8 @@ module VagrantPlugins
autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
autoload :DestroyDomain, action_root.join("destroy_domain")
autoload :StartDomain, action_root.join("start_domain")
autoload :SuspendDomain, action_root.join("suspend_domain")
autoload :ResumeDomain, action_root.join("resume_domain")
autoload :ReadState, action_root.join("read_state")
autoload :ReadSSHInfo, action_root.join("read_ssh_info")
autoload :TimedProvision, action_root.join("timed_provision")

View File

@ -48,11 +48,9 @@ module VagrantPlugins
conn_attr[:libvirt_password] = config.password if config.password
# Setup command for retrieving IP address for newly created machine
# with some MAC address. Get it via arp table. This solution doesn't
# require arpwatch to be installed.
conn_attr[:libvirt_ip_command] = "arp -an | grep $mac | sed '"
conn_attr[:libvirt_ip_command] << 's/.*(\([0-9\.]*\)).*/\1/'
conn_attr[:libvirt_ip_command] << "'"
# with some MAC address. Get it from dnsmasq leases table.
conn_attr[:libvirt_ip_command] = "cat /var/lib/libvirt/dnsmasq/*.leases"
conn_attr[:libvirt_ip_command] << " | grep $mac | awk ' { print $3 }'"
@logger.info("Connecting to Libvirt (#{uri}) ...")
begin

View File

@ -37,6 +37,7 @@ module VagrantPlugins
env[:ui].info(" -- Cpus: #{@cpus}")
env[:ui].info(" -- Memory: #{@memory_size/1024}M")
env[:ui].info(" -- Base box: #{env[:machine].box.name}")
env[:ui].info(" -- Storage pool: #{env[:machine].provider_config.storage_pool_name}")
env[:ui].info(" -- Image: #{@domain_volume_path}")
# Create libvirt domain.

View File

@ -4,24 +4,26 @@ module VagrantPlugins
module Libvirt
module Action
class DestroyDomain
def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_domain")
@app = app
end
def call(env)
# Destroy the server and remove the tracking ID
# Destroy the server, remove the tracking ID and file holding IP
# address.
env[:ui].info(I18n.t("vagrant_libvirt.destroy_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain.destroy(:destroy_volumes => true)
env[:machine].id = nil
# Remove file holding IP address
ip_file_path = env[:machine].data_dir + 'ip'
File.delete(ip_file_path) if File.exists?(ip_file_path)
@app.call(env)
end
end
end
end

View File

@ -18,23 +18,58 @@ module VagrantPlugins
@app.call(env)
end
def read_ssh_info(libvirt, machine)
def read_ssh_info(libvirt,machine)
return nil if machine.id.nil?
# Find the machine
server = libvirt.servers.get(machine.id)
if server.nil?
domain = libvirt.servers.get(machine.id)
if domain.nil?
# The machine can't be found
@logger.info("Machine couldn't be found, assuming it got destroyed.")
machine.id = nil
return nil
end
# Get ip address of machine
ip_address = server.public_ip_address
ip_address = server.private_ip_address if ip_address == nil
return nil if ip_address == nil
# IP address of machine is stored in $data_dir/ip file. Why? Commands
# like ssh or provision need to get IP of VM long time after it was
# started and gathered IP. Record in arp table is lost, and this is
# the way how to store this info. Not an ideal solution, but libvirt
# doesn't provide way how to get IP of some domain.
ip_file = machine.data_dir + 'ip'
raise Errors::NoIpAddressError if not File.exists?(ip_file)
ip_address = File.open(ip_file, 'r') do |file|
file.read
end
# Check if stored IP address matches with MAC address of machine.
# Command is executed either localy, or on remote libvirt hypervisor,
# depends on establised fog libvirt connection.
ip_match = false
ip_command = "ping -c1 #{ip_address} > /dev/null && "
ip_command << "arp -an | grep $mac | sed '"
ip_command << 's/.*(\([0-9\.]*\)).*/\1/' + "'"
options_hash = { :ip_command => ip_command }
3.times do |x|
break if ip_match
domain.wait_for(1) {
begin
addresses(service, options_hash).each_pair do |type, ip|
if ip[0] != nil
ip_match = true
break
end
end
rescue Fog::Errors::Error
# Sometimes, if pinging happen too quickly after after IP
# assignment, machine is not responding yet. Give it a little
# time..
sleep 1
end
break if ip_match
}
end
raise Errors::IpAddressMismatchError if not ip_match
# Return the info
# TODO: Some info should be configurable in Vagrantfile
@ -43,8 +78,7 @@ module VagrantPlugins
:port => 22,
:username => 'root',
}
end
end
end
end
end

View File

@ -0,0 +1,27 @@
require 'log4r'
module VagrantPlugins
module Libvirt
module Action
# Resume suspended domain.
class ResumeDomain
def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::resume_domain")
@app = app
end
def call(env)
env[:ui].info(I18n.t("vagrant_libvirt.resuming_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
domain.resume
@logger.info("Machine #{env[:machine].id} is resumed.")
@app.call(env)
end
end
end
end
end

View File

@ -3,23 +3,26 @@ require 'log4r'
module VagrantPlugins
module Libvirt
module Action
class Suspend
# Suspend domain.
class SuspendDomain
def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_domain")
@logger = Log4r::Logger.new("vagrant_libvirt::action::suspend_domain")
@app = app
end
# make pause
def call(env)
vmid = env[:machine].id.to_s.chomp
domain = env[:libvirt_compute].servers.get(vmid)
env[:ui].info(I18n.t("vagrant_libvirt.suspending_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
domain.suspend
@logger.info("Machine #{vmid} is suspended ")
@logger.info("Machine #{env[:machine].id} is suspended ")
@app.call(env)
end
end
end
end
end

View File

@ -47,6 +47,13 @@ module VagrantPlugins
@logger.info("Got IP address #{env[:ip_address]}")
@logger.info("Time for getting IP: #{env[:metrics]["instance_ip_time"]}")
# Save newly assigned IP address to machines data_dir
ip_file_path = env[:machine].data_dir + 'ip'
@logger.info("Saving IP address to #{ip_file_path} file.")
File.open(ip_file_path, 'w') do |file|
file.write(env[:ip_address])
end
# Machine has ip address assigned, now wait till we are able to
# connect via ssh.
env[:metrics]["instance_ssh_time"] = Util::Timer.time do

View File

@ -85,6 +85,15 @@ module VagrantPlugins
class AttachDeviceError < VagrantLibvirtError
error_key(:attach_device_error)
end
class NoIpAddressError < VagrantLibvirtError
error_key(:no_ip_address_error)
end
class IpAddressMismatchError < VagrantLibvirtError
error_key(:ip_address_mismatch_error)
end
end
end
end

View File

@ -1,13 +1,13 @@
en:
vagrant_libvirt:
already_created: |-
The machine is already created.
The domain is already created.
not_created: |-
Machine is not created. Please run `vagrant up` first.
Domain is not created. Please run `vagrant up` first.
finding_volume: |-
Checking if volume is available.
creating_domain: |-
Creating machine with the following settings...
Creating domain with the following settings...
uploading_volume: |-
Uploading base box image as volume into libvirt storage...
creating_domain_volume: |-
@ -15,17 +15,21 @@ en:
removing_domain_volume: |-
Removing image (snapshot of base box volume).
starting_domain: |-
Starting machine.
Starting domain.
terminating: |-
Removing machine...
Removing domain...
poweroff_domain: |-
Poweroff machine.
Poweroff domain.
destroy_domain: |-
Removing machine...
Removing domain...
resuming_domain: |-
Resuming domain...
suspending_domain: |-
Suspending domain...
waiting_for_ready: |-
Waiting for machine to become "ready"...
Waiting for domain to become "ready"...
waiting_for_ip: |-
Waiting for machine to get an IP address...
Waiting for domain to get an IP address...
waiting_for_ssh: |-
Waiting for SSH to become available...
booted: |-
@ -89,6 +93,12 @@ en:
No domain found. %{error_message}
attach_device_error: |-
Error while attaching new device to domain. %{error_message}
no_ip_address_error: |-
No IP address found.
ip_address_mismatch_error: |-
IP address mismatch. Possible reason is that domain IP address changed.
You can run `vagrant destroy` and then `vagrant up` to rebuild your
project.
states:
short_paused: |-