- 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
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env rake #!/usr/bin/env rake
require 'rubygems' #require 'rubygems'
require 'bundler/setup' #require 'bundler/setup'
#require 'bundler/gem_tasks' require 'bundler/gem_tasks'
Bundler::GemHelper.install_tasks Bundler::GemHelper.install_tasks
+72 -16
View File
@@ -29,12 +29,12 @@ module VagrantPlugins
b2.use CreateDomainVolume b2.use CreateDomainVolume
b2.use CreateDomain b2.use CreateDomain
b2.use CreateNetworkInterfaces b2.use CreateNetworkInterfaces
end
b.use TimedProvision b2.use TimedProvision
b.use StartDomain b2.use StartDomain
b.use WaitTillUp b2.use WaitTillUp
b.use SyncFolders b2.use SyncFolders
end
end end
end end
@@ -55,6 +55,71 @@ module VagrantPlugins
end end
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 # 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. # state is expected to be put into the `:machine_state_id` key.
def self.action_read_state def self.action_read_state
@@ -76,18 +141,7 @@ module VagrantPlugins
end end
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__)) action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :Suspend, action_root.join("suspend")
autoload :ConnectLibvirt, action_root.join("connect_libvirt") autoload :ConnectLibvirt, action_root.join("connect_libvirt")
autoload :IsCreated, action_root.join("is_created") autoload :IsCreated, action_root.join("is_created")
autoload :MessageAlreadyCreated, action_root.join("message_already_created") autoload :MessageAlreadyCreated, action_root.join("message_already_created")
@@ -100,6 +154,8 @@ module VagrantPlugins
autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces") autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
autoload :DestroyDomain, action_root.join("destroy_domain") autoload :DestroyDomain, action_root.join("destroy_domain")
autoload :StartDomain, action_root.join("start_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 :ReadState, action_root.join("read_state")
autoload :ReadSSHInfo, action_root.join("read_ssh_info") autoload :ReadSSHInfo, action_root.join("read_ssh_info")
autoload :TimedProvision, action_root.join("timed_provision") autoload :TimedProvision, action_root.join("timed_provision")
@@ -48,11 +48,9 @@ module VagrantPlugins
conn_attr[:libvirt_password] = config.password if config.password conn_attr[:libvirt_password] = config.password if config.password
# Setup command for retrieving IP address for newly created machine # Setup command for retrieving IP address for newly created machine
# with some MAC address. Get it via arp table. This solution doesn't # with some MAC address. Get it from dnsmasq leases table.
# require arpwatch to be installed. conn_attr[:libvirt_ip_command] = "cat /var/lib/libvirt/dnsmasq/*.leases"
conn_attr[:libvirt_ip_command] = "arp -an | grep $mac | sed '" conn_attr[:libvirt_ip_command] << " | grep $mac | awk ' { print $3 }'"
conn_attr[:libvirt_ip_command] << 's/.*(\([0-9\.]*\)).*/\1/'
conn_attr[:libvirt_ip_command] << "'"
@logger.info("Connecting to Libvirt (#{uri}) ...") @logger.info("Connecting to Libvirt (#{uri}) ...")
begin begin
@@ -37,6 +37,7 @@ module VagrantPlugins
env[:ui].info(" -- Cpus: #{@cpus}") env[:ui].info(" -- Cpus: #{@cpus}")
env[:ui].info(" -- Memory: #{@memory_size/1024}M") env[:ui].info(" -- Memory: #{@memory_size/1024}M")
env[:ui].info(" -- Base box: #{env[:machine].box.name}") 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}") env[:ui].info(" -- Image: #{@domain_volume_path}")
# Create libvirt domain. # Create libvirt domain.
+6 -4
View File
@@ -4,24 +4,26 @@ module VagrantPlugins
module Libvirt module Libvirt
module Action module Action
class DestroyDomain class DestroyDomain
def initialize(app, env) def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_domain") @logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_domain")
@app = app @app = app
end end
def call(env) def call(env)
# Destroy the server, remove the tracking ID and file holding IP
# Destroy the server and remove the tracking ID # address.
env[:ui].info(I18n.t("vagrant_libvirt.destroy_domain")) env[:ui].info(I18n.t("vagrant_libvirt.destroy_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s) domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain.destroy(:destroy_volumes => true) domain.destroy(:destroy_volumes => true)
env[:machine].id = nil 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) @app.call(env)
end end
end end
end end
end end
+44 -10
View File
@@ -18,23 +18,58 @@ module VagrantPlugins
@app.call(env) @app.call(env)
end end
def read_ssh_info(libvirt,machine)
def read_ssh_info(libvirt, machine)
return nil if machine.id.nil? return nil if machine.id.nil?
# Find the machine # Find the machine
server = libvirt.servers.get(machine.id) domain = libvirt.servers.get(machine.id)
if server.nil? if domain.nil?
# The machine can't be found # The machine can't be found
@logger.info("Machine couldn't be found, assuming it got destroyed.") @logger.info("Machine couldn't be found, assuming it got destroyed.")
machine.id = nil machine.id = nil
return nil return nil
end end
# Get ip address of machine # IP address of machine is stored in $data_dir/ip file. Why? Commands
ip_address = server.public_ip_address # like ssh or provision need to get IP of VM long time after it was
ip_address = server.private_ip_address if ip_address == nil # started and gathered IP. Record in arp table is lost, and this is
return nil if ip_address == nil # 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 # Return the info
# TODO: Some info should be configurable in Vagrantfile # TODO: Some info should be configurable in Vagrantfile
@@ -43,8 +78,7 @@ module VagrantPlugins
:port => 22, :port => 22,
:username => 'root', :username => 'root',
} }
end end
end end
end end
end end
@@ -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
@@ -3,23 +3,26 @@ require 'log4r'
module VagrantPlugins module VagrantPlugins
module Libvirt module Libvirt
module Action module Action
# Suspend domain.
class Suspend class SuspendDomain
def initialize(app, env) def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_domain") @logger = Log4r::Logger.new("vagrant_libvirt::action::suspend_domain")
@app = app @app = app
end end
# make pause # make pause
def call(env) def call(env)
vmid = env[:machine].id.to_s.chomp env[:ui].info(I18n.t("vagrant_libvirt.suspending_domain"))
domain = env[:libvirt_compute].servers.get(vmid)
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
domain.suspend domain.suspend
@logger.info("Machine #{vmid} is suspended ") @logger.info("Machine #{env[:machine].id} is suspended ")
@app.call(env) @app.call(env)
end end
end end
end end
end end
end end
@@ -47,6 +47,13 @@ module VagrantPlugins
@logger.info("Got IP address #{env[:ip_address]}") @logger.info("Got IP address #{env[:ip_address]}")
@logger.info("Time for getting IP: #{env[:metrics]["instance_ip_time"]}") @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 # Machine has ip address assigned, now wait till we are able to
# connect via ssh. # connect via ssh.
env[:metrics]["instance_ssh_time"] = Util::Timer.time do env[:metrics]["instance_ssh_time"] = Util::Timer.time do
+9
View File
@@ -85,6 +85,15 @@ module VagrantPlugins
class AttachDeviceError < VagrantLibvirtError class AttachDeviceError < VagrantLibvirtError
error_key(:attach_device_error) error_key(:attach_device_error)
end end
class NoIpAddressError < VagrantLibvirtError
error_key(:no_ip_address_error)
end
class IpAddressMismatchError < VagrantLibvirtError
error_key(:ip_address_mismatch_error)
end
end end
end end
end end
+19 -9
View File
@@ -1,13 +1,13 @@
en: en:
vagrant_libvirt: vagrant_libvirt:
already_created: |- already_created: |-
The machine is already created. The domain is already created.
not_created: |- not_created: |-
Machine is not created. Please run `vagrant up` first. Domain is not created. Please run `vagrant up` first.
finding_volume: |- finding_volume: |-
Checking if volume is available. Checking if volume is available.
creating_domain: |- creating_domain: |-
Creating machine with the following settings... Creating domain with the following settings...
uploading_volume: |- uploading_volume: |-
Uploading base box image as volume into libvirt storage... Uploading base box image as volume into libvirt storage...
creating_domain_volume: |- creating_domain_volume: |-
@@ -15,17 +15,21 @@ en:
removing_domain_volume: |- removing_domain_volume: |-
Removing image (snapshot of base box volume). Removing image (snapshot of base box volume).
starting_domain: |- starting_domain: |-
Starting machine. Starting domain.
terminating: |- terminating: |-
Removing machine... Removing domain...
poweroff_domain: |- poweroff_domain: |-
Poweroff machine. Poweroff domain.
destroy_domain: |- destroy_domain: |-
Removing machine... Removing domain...
resuming_domain: |-
Resuming domain...
suspending_domain: |-
Suspending domain...
waiting_for_ready: |- waiting_for_ready: |-
Waiting for machine to become "ready"... Waiting for domain to become "ready"...
waiting_for_ip: |- 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: |-
Waiting for SSH to become available... Waiting for SSH to become available...
booted: |- booted: |-
@@ -89,6 +93,12 @@ en:
No domain found. %{error_message} No domain found. %{error_message}
attach_device_error: |- attach_device_error: |-
Error while attaching new device to domain. %{error_message} 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: states:
short_paused: |- short_paused: |-