Move libvirt connection setup to driver

Handle libvirt connection through a driver located within the provider
so it can be reached via the machine settings. Adopt the format followed
by the docker/virtualbox providers as this is likely to remain well
supported.

Will allow queries to be made without needing to setup a specific action
which is important when dealing with parallel machine provisioning.
Calling actions from other threads to retrieve information on the state
of the other running machines currently will cause vagrant to complain
about the machine being locked.
This commit is contained in:
Darragh Bailey 2015-07-28 09:57:48 +01:00
parent a46545e66c
commit db440907f7
29 changed files with 103 additions and 123 deletions

View File

@ -8,20 +8,6 @@ module VagrantPlugins
autoload :Errors, lib_path.join('errors')
autoload :Util, lib_path.join('util')
# Hold connection handler so there is no need to connect more times than
# one. This can be annoying when there are more machines to create, or when
# doing state action first and then some other.
#
# TODO Don't sure if this is the best solution
@@libvirt_connection = nil
def self.libvirt_connection
@@libvirt_connection
end
def self.libvirt_connection=(conn)
@@libvirt_connection = conn
end
def self.source_root
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
end

View File

@ -19,7 +19,6 @@ module VagrantPlugins
def self.action_up
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use Call, IsCreated do |env, b2|
# Create VM if not yet created.
if !env[:result]
@ -58,7 +57,6 @@ module VagrantPlugins
def self.action_start
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use Call, IsRunning do |env, b2|
# If the VM is running, then our work here is done, exit
next if env[:result]
@ -101,7 +99,6 @@ module VagrantPlugins
def self.action_halt
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use ClearForwardedPorts
b.use Call, IsCreated do |env, b2|
if !env[:result]
@ -144,7 +141,6 @@ module VagrantPlugins
def self.action_package
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use PackageDomain
end
end
@ -157,7 +153,6 @@ module VagrantPlugins
b.use Call, IsCreated do |env, b2|
if !env[:result]
# Try to remove stale volumes anyway
b2.use ConnectLibvirt
b2.use SetNameOfDomain
b2.use RemoveStaleVolume
if !env[:result]
@ -167,7 +162,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use ClearForwardedPorts
# b2.use PruneNFSExports
b2.use DestroyDomain
@ -186,7 +180,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use Call, IsRunning do |env2, b3|
if !env2[:result]
b3.use MessageNotRunning
@ -209,7 +202,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use Call, IsRunning do |env2, b3|
if !env2[:result]
b3.use MessageNotRunning
@ -234,7 +226,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use Call, IsRunning do |env2, b3|
if !env2[:result]
b3.use MessageNotRunning
@ -257,7 +248,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use Call, IsSuspended do |env2, b3|
if !env2[:result]
b3.use MessageNotSuspended
@ -274,7 +264,6 @@ module VagrantPlugins
def self.action_read_state
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use ReadState
end
end
@ -285,7 +274,6 @@ module VagrantPlugins
def self.action_read_ssh_info
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use ReadSSHInfo
end
end
@ -293,7 +281,6 @@ module VagrantPlugins
def self.action_read_mac_addresses
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use ConnectLibvirt
b.use ReadMacAddresses
end
end
@ -308,7 +295,6 @@ module VagrantPlugins
next
end
b2.use ConnectLibvirt
b2.use Call, IsRunning do |env2, b3|
if !env2[:result]
b3.use MessageNotRunning
@ -323,7 +309,6 @@ module VagrantPlugins
end
action_root = Pathname.new(File.expand_path('../action', __FILE__))
autoload :ConnectLibvirt, action_root.join('connect_libvirt')
autoload :PackageDomain, action_root.join('package_domain')
autoload :CreateDomain, action_root.join('create_domain')
autoload :CreateDomainVolume, action_root.join('create_domain_volume')

View File

@ -1,51 +0,0 @@
require 'fog/libvirt'
require 'log4r'
module VagrantPlugins
module ProviderLibvirt
module Action
class ConnectLibvirt
def initialize(app, env)
@logger = Log4r::Logger.new('vagrant_libvirt::action::connect_libvirt')
@app = app
end
def call(env)
# If already connected to libvirt, just use it and don't connect
# again.
if ProviderLibvirt.libvirt_connection
env[:libvirt_compute] = ProviderLibvirt.libvirt_connection
return @app.call(env)
end
# Get config options for libvirt provider.
config = env[:machine].provider_config
uri = config.uri
conn_attr = {}
conn_attr[:provider] = 'libvirt'
conn_attr[:libvirt_uri] = uri
conn_attr[:libvirt_username] = config.username if config.username
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 from dnsmasq leases table
ip_command = %q[ awk "/$mac/ {print \$1}" /proc/net/arp ]
conn_attr[:libvirt_ip_command] = ip_command
@logger.info("Connecting to Libvirt (#{uri}) ...")
begin
env[:libvirt_compute] = Fog::Compute.new(conn_attr)
rescue Fog::Errors::Error => e
raise Errors::FogLibvirtConnectionError,
:error_message => e.message
end
ProviderLibvirt.libvirt_connection = env[:libvirt_compute]
@app.call(env)
end
end
end
end
end

View File

@ -69,7 +69,7 @@ module VagrantPlugins
@os_type = 'hvm'
# Get path to domain image from the storage pool selected.
actual_volumes = env[:libvirt_compute].volumes.all.select do |x|
actual_volumes = env[:machine].provider.driver.connection.volumes.all.select do |x|
x.pool_name == @storage_pool_name
end
domain_volume = ProviderLibvirt::Util::Collection.find_matching(
@ -91,12 +91,12 @@ module VagrantPlugins
disk[:absolute_path] = storage_prefix + disk[:path]
if env[:libvirt_compute].volumes.select {
if env[:machine].provider.driver.connection.volumes.select {
|x| x.name == disk[:name] and x.pool_name == @storage_pool_name}.empty?
# make the disk. equivalent to:
# qemu-img create -f qcow2 <path> 5g
begin
env[:libvirt_compute].volumes.create(
env[:machine].provider.driver.connection.volumes.create(
name: disk[:name],
format_type: disk[:type],
path: disk[:absolute_path],
@ -161,7 +161,7 @@ module VagrantPlugins
# Is there a way to tell fog to create new domain with already
# existing volume? Use domain creation from template..
begin
server = env[:libvirt_compute].servers.create(
server = env[:machine].provider.driver.connection.servers.create(
xml: to_xml('domain'))
rescue Fog::Errors::Error => e
raise Errors::FogCreateServerError, error_message: e.message

View File

@ -26,12 +26,12 @@ module VagrantPlugins
# Verify the volume doesn't exist already.
domain_volume = ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].volumes.all, @name)
env[:machine].provider.driver.connection.volumes.all, @name)
raise Errors::DomainVolumeExists if domain_volume
# Get path to backing image - box volume.
box_volume = ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].volumes.all, env[:box_volume_name])
env[:machine].provider.driver.connection.volumes.all, env[:box_volume_name])
@backing_file = box_volume.path
# Virtual size of image. Take value worked out by HandleBoxImage
@ -40,7 +40,7 @@ module VagrantPlugins
# Create new volume from xml template. Fog currently doesn't support
# volume snapshots directly.
begin
domain_volume = env[:libvirt_compute].volumes.create(
domain_volume = env[:machine].provider.driver.connection.volumes.create(
:xml => to_xml('volume_snapshot'),
:pool_name => config.storage_pool_name)
rescue Fog::Errors::Error => e

View File

@ -27,7 +27,7 @@ module VagrantPlugins
def call(env)
# Get domain first.
begin
domain = env[:libvirt_compute].client.lookup_domain_by_uuid(
domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
env[:machine].id.to_s)
rescue => e
raise Errors::NoDomainError,
@ -61,7 +61,7 @@ module VagrantPlugins
# We have slot for interface, fill it with interface configuration.
adapters[free_slot] = options
adapters[free_slot][:network_name] = interface_network(
env[:libvirt_compute].client, adapters[free_slot])
env[:machine].provider.driver.connection.client, adapters[free_slot])
end
# Create each interface as new domain device.

View File

@ -23,7 +23,7 @@ module VagrantPlugins
@available_networks = []
@options = {}
@libvirt_client = env[:libvirt_compute].client
@libvirt_client = env[:machine].provider.driver.connection.client
end
def call(env)
@ -46,7 +46,7 @@ module VagrantPlugins
# list is used throughout this class and should be easier to
# process than libvirt API calls.
@available_networks = libvirt_networks(
env[:libvirt_compute].client)
env[:machine].provider.driver.connection.client)
# Prepare a hash describing network for this specific interface.
@interface_network = {

View File

@ -17,7 +17,7 @@ module VagrantPlugins
# Fog libvirt currently doesn't support snapshots. Use
# ruby-libvirt client directly. Note this is racy, see
# http://www.libvirt.org/html/libvirt-libvirt.html#virDomainSnapshotListNames
libvirt_domain = env[:libvirt_compute].client.lookup_domain_by_uuid(
libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
env[:machine].id)
libvirt_domain.list_snapshots.each do |name|
@logger.info("Deleting snapshot '#{name}'")
@ -28,7 +28,7 @@ module VagrantPlugins
end
end
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
if env[:machine].provider_config.disks.empty?
# if using default configuration of disks

View File

@ -36,7 +36,7 @@ module VagrantPlugins
# lookup_network_by_uuid throws same exception
# if there is an error or if the network just doesn't exist
begin
libvirt_network = env[:libvirt_compute].client.lookup_network_by_uuid(
libvirt_network = env[:machine].provider.driver.connection.client.lookup_network_by_uuid(
network_uuid)
rescue Libvirt::RetrieveError => e
# this network is already destroyed, so move on

View File

@ -13,7 +13,7 @@ module VagrantPlugins
def call(env)
env[:ui].info(I18n.t("vagrant_libvirt.halt_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
@logger.info("Trying gracefull shutdown.")

View File

@ -60,7 +60,7 @@ module VagrantPlugins
@@lock.synchronize do
# Don't continue if image already exists in storage pool.
break if ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].volumes.all, env[:box_volume_name])
env[:machine].provider.driver.connection.volumes.all, env[:box_volume_name])
# Box is not available as a storage pool volume. Create and upload
# it as a copy of local box image.
@ -73,7 +73,7 @@ module VagrantPlugins
message << " in storage pool #{config.storage_pool_name}."
@logger.info(message)
begin
fog_volume = env[:libvirt_compute].volumes.create(
fog_volume = env[:machine].provider.driver.connection.volumes.create(
name: env[:box_volume_name],
allocation: "#{box_image_size/1024/1024}M",
capacity: "#{box_virtual_size}G",
@ -117,10 +117,10 @@ module VagrantPlugins
image_size = File.size(image_file) # B
begin
pool = env[:libvirt_compute].client.lookup_storage_pool_by_name(
pool = env[:machine].provider.driver.connection.client.lookup_storage_pool_by_name(
pool_name)
volume = pool.lookup_volume_by_name(volume_name)
stream = env[:libvirt_compute].client.stream
stream = env[:machine].provider.driver.connection.client.stream
volume.upload(stream, offset=0, length=image_size)
# Exception ProviderLibvirt::RetrieveError can be raised if buffer is

View File

@ -23,7 +23,7 @@ module VagrantPlugins
@@lock.synchronize do
# Check for storage pool, where box image should be created
break if ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].pools.all, config.storage_pool_name)
env[:machine].provider.driver.connection.pools.all, config.storage_pool_name)
@logger.info("No storage pool '#{config.storage_pool_name}' is available.")
@ -36,7 +36,7 @@ module VagrantPlugins
# Fog libvirt currently doesn't support creating pools. Use
# ruby-libvirt client directly.
begin
libvirt_pool = env[:libvirt_compute].client.define_storage_pool_xml(
libvirt_pool = env[:machine].provider.driver.connection.client.define_storage_pool_xml(
to_xml('default_storage_pool'))
libvirt_pool.build
libvirt_pool.create

View File

@ -9,7 +9,7 @@ module VagrantPlugins
end
def call(env)
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
env[:result] = domain.state.to_s == 'running'

View File

@ -9,7 +9,7 @@ module VagrantPlugins
end
def call(env)
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
env[:result] = domain.state.to_s == 'paused'

View File

@ -14,9 +14,9 @@ module VagrantPlugins
def call(env)
env[:ui].info(I18n.t('vagrant_libvirt.package_domain'))
libvirt_domain = env[:libvirt_compute].client.lookup_domain_by_uuid(
libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
env[:machine].id)
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
root_disk = domain.volumes.select do |x|
x.name == libvirt_domain.name + '.img'
end.first

View File

@ -8,7 +8,7 @@ module VagrantPlugins
end
def call(env)
env[:nfs_valid_ids] = env[:libvirt_compute].servers.all.map(&:id)
env[:nfs_valid_ids] = env[:machine].provider.driver.connection.servers.all.map(&:id)
@app.call(env)
end
end

View File

@ -12,7 +12,7 @@ module VagrantPlugins
if env[:host]
uuid = env[:machine].id
# get all uuids
uuids = env[:libvirt_compute].servers.all.map(&:id)
uuids = env[:machine].provider.driver.connection.servers.all.map(&:id)
# not exiisted in array will removed from nfs
uuids.delete(uuid)
env[:host].capability(

View File

@ -10,7 +10,7 @@ module VagrantPlugins
end
def call(env)
env[:machine_mac_addresses] = read_mac_addresses(env[:libvirt_compute], env[:machine])
env[:machine_mac_addresses] = read_mac_addresses(env[:machine].provider.driver.connection, env[:machine])
end
def read_mac_addresses(libvirt, machine)

View File

@ -12,7 +12,7 @@ module VagrantPlugins
end
def call(env)
env[:machine_ssh_info] = read_ssh_info(env[:libvirt_compute],
env[:machine_ssh_info] = read_ssh_info(env[:machine].provider.driver.connection,
env[:machine])
@app.call(env)

View File

@ -12,7 +12,7 @@ module VagrantPlugins
end
def call(env)
env[:machine_state_id] = read_state(env[:libvirt_compute], env[:machine])
env[:machine_state_id] = read_state(env[:machine].provider.driver.connection, env[:machine])
@app.call(env)
end

View File

@ -22,7 +22,7 @@ module VagrantPlugins
config = env[:machine].provider_config
# Check for storage pool, where box image should be created
fog_pool = ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].pools.all, config.storage_pool_name)
env[:machine].provider.driver.connection.pools.all, config.storage_pool_name)
@logger.debug("**** Pool #{fog_pool.name}")
# This is name of newly created image for vm.
@ -31,7 +31,7 @@ module VagrantPlugins
# remove root storage
box_volume = ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].volumes.all, name)
env[:machine].provider.driver.connection.volumes.all, name)
if box_volume && box_volume.pool_name == fog_pool.name
@logger.info("Deleting volume #{box_volume.key}")
box_volume.destroy

View File

@ -13,7 +13,7 @@ module VagrantPlugins
def call(env)
env[:ui].info(I18n.t("vagrant_libvirt.resuming_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
domain.resume

View File

@ -14,11 +14,12 @@ module VagrantPlugins
env[:domain_name] = build_domain_name(env)
begin
@logger.info("Looking for domain #{env[:domain_name]} through list #{env[:libvirt_compute].servers.all}")
@logger.info("Looking for domain #{env[:domain_name]} through list " +
"#{env[:machine].provider.driver.connection.servers.all}")
# Check if the domain name is not already taken
domain = ProviderLibvirt::Util::Collection.find_matching(
env[:libvirt_compute].servers.all, env[:domain_name])
env[:machine].provider.driver.connection.servers.all, env[:domain_name])
rescue Fog::Errors::Error => e
@logger.info("#{e}")
domain = nil

View File

@ -14,7 +14,7 @@ module VagrantPlugins
def call(env)
env[:ui].info(I18n.t("vagrant_libvirt.starting_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
begin

View File

@ -14,7 +14,7 @@ module VagrantPlugins
def call(env)
env[:ui].info(I18n.t("vagrant_libvirt.suspending_domain"))
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise Errors::NoDomainError if domain == nil
domain.suspend

View File

@ -21,7 +21,7 @@ module VagrantPlugins
env[:metrics] ||= {}
# Get domain object
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
raise NoDomainError if domain == nil
# Wait for domain to obtain an ip address. Ip address is searched

View File

@ -25,13 +25,13 @@ module VagrantPlugins
# <filesystem/> support in device attach/detach introduced in 1.2.2
# version number format is major * 1,000,000 + minor * 1,000 + release
libvirt_version = ProviderLibvirt.libvirt_connection.client.libversion
libvirt_version = machine.provider.driver.connection.client.libversion
libvirt_version >= 1_002_002
end
def prepare(machine, folders, _opts)
raise Vagrant::Errors::Error('No libvirt connection') if ProviderLibvirt.libvirt_connection.nil?
@conn = ProviderLibvirt.libvirt_connection.client
raise Vagrant::Errors::Error('No libvirt connection') if machine.provider.driver.connection.nil?
@conn = machine.provider.driver.connection.client
begin
# loop through folders
@ -73,10 +73,10 @@ module VagrantPlugins
end
def cleanup(machine, _opts)
if ProviderLibvirt.libvirt_connection.nil?
if machine.provider.driver.connection.nil?
raise Vagrant::Errors::Error('No libvirt connection')
end
@conn = ProviderLibvirt.libvirt_connection.client
@conn = machine.provider.driver.connection.client
begin
if machine.id && machine.id != ''
dom = @conn.lookup_domain_by_uuid(machine.id)

View File

@ -0,0 +1,52 @@
require 'fog/libvirt'
require 'log4r'
module VagrantPlugins
module ProviderLibvirt
class Driver
# store the connection at the process level
#
# possibly this should be a connection pool using the connection
# settings as a key to allow per machine connection attributes
# to be used.
@@connection = nil
def initialize(machine)
@logger = Log4r::Logger.new('vagrant_libvirt::driver')
@machine = machine
end
def connection
# If already connected to libvirt, just use it and don't connect
# again.
return @@connection if @@connection
# Get config options for libvirt provider.
config = @machine.provider_config
uri = config.uri
conn_attr = {}
conn_attr[:provider] = 'libvirt'
conn_attr[:libvirt_uri] = uri
conn_attr[:libvirt_username] = config.username if config.username
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 from dnsmasq leases table
ip_command = %q[ awk "/$mac/ {print \$1}" /proc/net/arp ]
conn_attr[:libvirt_ip_command] = ip_command
@logger.info("Connecting to Libvirt (#{uri}) ...")
begin
@@connection = Fog::Compute.new(conn_attr)
rescue Fog::Errors::Error => e
raise Errors::FogLibvirtConnectionError,
:error_message => e.message
end
@@connection
end
end
end
end

View File

@ -2,6 +2,7 @@ require 'vagrant'
module VagrantPlugins
module ProviderLibvirt
autoload :Driver, 'vagrant-libvirt/driver'
# This is the base class for a provider for the V2 API. A provider
# is responsible for creating compute resources to match the
@ -22,6 +23,12 @@ module VagrantPlugins
nil
end
def driver
return @driver if @driver
@driver = Driver.new(@machine)
end
# This method is called if the underying machine ID changes. Providers
# can use this method to load in new data for the actual backing
# machine or to realize that the machine is now gone (the ID can