Merge pull request #170 from teto/virtfs

Add 9p (virtfs) support to vagrant-libvirt.
This commit is contained in:
Dmitry Vasilets 2014-03-23 20:08:51 +01:00
commit 489e096227
8 changed files with 172 additions and 82 deletions

View File

@ -10,3 +10,7 @@ group :development do
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git" gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
end end
group :plugins do
gem "vagrant-libvirt", :path => '.'
end

View File

@ -27,19 +27,26 @@ module VagrantPlugins
b2.use CreateNetworks b2.use CreateNetworks
b2.use CreateNetworkInterfaces b2.use CreateNetworkInterfaces
b2.use StartDomain
b2.use WaitTillUp
b2.use PrepareNFSValidIds b2.use PrepareNFSValidIds
b2.use SyncedFolderCleanup b2.use SyncedFolderCleanup
b2.use SyncedFolders b2.use SyncedFolders
b2.use StartDomain
b2.use WaitTillUp
b2.use StartDomain
b2.use WaitTillUp
b2.use ForwardPorts b2.use ForwardPorts
b2.use PrepareNFSSettings # b2.use PrepareNFSSettings
b2.use ShareFolders b2.use ShareFolders
b2.use SetHostname b2.use SetHostname
b2.use SyncFolders # b2.use SyncFolders
else else
b2.use action_start b2.use action_start
end end
@ -70,6 +77,11 @@ module VagrantPlugins
# Ensure networks are created and active # Ensure networks are created and active
b3.use CreateNetworks b3.use CreateNetworks
b3.use PrepareNFSValidIds
b3.use SyncedFolderCleanup
b3.use SyncedFolders
# Start it.. # Start it..
b3.use StartDomain b3.use StartDomain
@ -77,12 +89,9 @@ module VagrantPlugins
# so wait for dhcp lease and store IP into machines data_dir. # so wait for dhcp lease and store IP into machines data_dir.
b3.use WaitTillUp b3.use WaitTillUp
b3.use PrepareNFSValidIds
b3.use SyncedFolderCleanup
b3.use SyncedFolders
b3.use ForwardPorts b3.use ForwardPorts
b3.use PrepareNFSSettings # b3.use PrepareNFSSettings
b3.use ShareFolders b3.use ShareFolders
end end
@ -147,7 +156,7 @@ module VagrantPlugins
b2.use ConnectLibvirt b2.use ConnectLibvirt
b2.use ClearForwardedPorts b2.use ClearForwardedPorts
b2.use PruneNFSExports # b2.use PruneNFSExports
b2.use DestroyDomain b2.use DestroyDomain
b2.use DestroyNetworks b2.use DestroyNetworks
end end
@ -195,7 +204,7 @@ module VagrantPlugins
end end
b3.use Provision b3.use Provision
b3.use SyncFolders # b3.use SyncFolders
end end
end end
end end
@ -312,18 +321,25 @@ module VagrantPlugins
autoload :MessageNotCreated, action_root.join('message_not_created') autoload :MessageNotCreated, action_root.join('message_not_created')
autoload :MessageNotRunning, action_root.join('message_not_running') autoload :MessageNotRunning, action_root.join('message_not_running')
autoload :MessageNotSuspended, action_root.join('message_not_suspended') autoload :MessageNotSuspended, action_root.join('message_not_suspended')
autoload :PrepareNFSSettings, action_root.join('prepare_nfs_settings') autoload :PrepareNFSSettings, action_root.join('prepare_nfs_settings')
autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids') autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
autoload :PruneNFSExports, action_root.join('prune_nfs_exports') autoload :PruneNFSExports, action_root.join('prune_nfs_exports')
autoload :ReadSSHInfo, action_root.join('read_ssh_info') autoload :ReadSSHInfo, action_root.join('read_ssh_info')
autoload :ReadState, action_root.join('read_state') autoload :ReadState, action_root.join('read_state')
autoload :ResumeDomain, action_root.join('resume_domain') autoload :ResumeDomain, action_root.join('resume_domain')
autoload :SetNameOfDomain, action_root.join('set_name_of_domain') autoload :SetNameOfDomain, action_root.join('set_name_of_domain')
# I don't think we need it anymore
autoload :ShareFolders, action_root.join('share_folders') autoload :ShareFolders, action_root.join('share_folders')
autoload :StartDomain, action_root.join('start_domain') autoload :StartDomain, action_root.join('start_domain')
autoload :SuspendDomain, action_root.join('suspend_domain') autoload :SuspendDomain, action_root.join('suspend_domain')
autoload :SyncFolders, action_root.join('sync_folders') autoload :TimedProvision, action_root.join('timed_provision')
autoload :WaitTillUp, action_root.join('wait_till_up') autoload :WaitTillUp, action_root.join('wait_till_up')
autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
autoload :SSHRun, 'vagrant/action/builtin/ssh_run' autoload :SSHRun, 'vagrant/action/builtin/ssh_run'
autoload :HandleBoxUrl, 'vagrant/action/builtin/handle_box_url' autoload :HandleBoxUrl, 'vagrant/action/builtin/handle_box_url'
autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders' autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders'

View File

@ -1,66 +0,0 @@
require "log4r"
require "vagrant/util/subprocess"
module VagrantPlugins
module ProviderLibvirt
module Action
# This middleware uses `rsync` to sync the folders over to the
# libvirt domain.
class SyncFolders
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant_libvirt::action::sync_folders")
end
def call(env)
@app.call(env)
ssh_info = env[:machine].ssh_info
env[:machine].config.vm.synced_folders.each do |id, data|
next unless data[:type] == :rsync
proxycommand = "-o ProxyCommand='#{ssh_info[:proxy_command]}'" if ssh_info[:proxy_command]
hostpath = File.expand_path(data[:hostpath], env[:root_path])
guestpath = data[:guestpath]
# Make sure there is a trailing slash on the host path to
# avoid creating an additional directory with rsync
hostpath = "#{hostpath}/" if hostpath !~ /\/$/
env[:ui].info(I18n.t('vagrant_libvirt.rsync_folder',
:hostpath => hostpath,
:guestpath => guestpath))
# Create the guest path
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
env[:machine].communicate.sudo(
"chown #{ssh_info[:username]} '#{guestpath}'")
# Rsync over to the guest path using the SSH info
command = [
'rsync', '--del', '--verbose', '--archive', '-z',
'--exclude', '.vagrant/',
'-e', "ssh -p #{ssh_info[:port]} #{proxycommand} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}",
hostpath,
"#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
r = Vagrant::Util::Subprocess.execute(*command)
if r.exit_code != 0
raise Errors::RsyncError,
:guestpath => guestpath,
:hostpath => hostpath,
:stderr => r.stderr
end
end
end
private
def ssh_key_options(ssh_info)
# Ensure that `private_key_path` is an Array (for Vagrant < 1.4)
Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join
end
end
end
end
end

View File

@ -0,0 +1,38 @@
require "vagrant/util/retryable"
module VagrantPlugins
module ProviderLibvirt
module Cap
class MountP9
extend Vagrant::Util::Retryable
def self.mount_p9_shared_folder(machine, folders, options)
folders.each do |name, opts|
# Expand the guest path so we can handle things like "~/vagrant"
expanded_guest_path = machine.guest.capability(
:shell_expand_guest_path, opts[:guestpath])
# Do the actual creating and mounting
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
# Mount
mount_tag = name.dup
mount_opts="-o trans=virtio"
mount_opts += ",access=#{options[:owner]}" if options[:owner]
mount_opts += ",version=#{options[:version]}" if options[:version]
mount_opts += ",#{opts[:mount_options]}" if opts[:mount_options]
mount_command = "mount -t 9p #{mount_opts} '#{mount_tag}' #{expanded_guest_path}"
retryable(:on => Vagrant::Errors::LinuxMountFailed,
:tries => 5,
:sleep => 3) do
machine.communicate.sudo(mount_command,
:error_class => Vagrant::Errors::LinuxMountFailed)
end
end
end
end
end
end
end

View File

@ -0,0 +1,75 @@
require "log4r"
require 'ostruct'
require "vagrant/util/subprocess"
require "vagrant/errors"
require "vagrant-libvirt/errors"
# require_relative "helper"
module VagrantPlugins
module SyncedFolder9p
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
include Vagrant::Util
include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
def initialize(*args)
super
@logger = Log4r::Logger.new("vagrant_libvirt::synced_folders::9p")
end
def usable?(machine, raise_error=false)
# TODO check for host support (eg in linux is 9p compiled ?)
# and support in Qemu for instance ?
machine.provider_name == :libvirt
end
def prepare(machine, folders, opts)
raise Vagrant::Errors::Error("No libvirt connection") if ProviderLibvirt.libvirt_connection.nil?
@conn = ProviderLibvirt.libvirt_connection.client
begin
# loop through folders
folders.each do |id, folder_opts|
folder_opts.merge!({ :accessmode => "passthrough",
:readonly => true })
# machine.ui.info "================\nMachine id: #{machine.id}Should be mounting folders\n #{id}, opts: #{folder_opts}"
xml = to_xml('filesystem', folder_opts )
# puts "<<<<< XML:\n #{xml}\n >>>>>"
@conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0)
end
rescue => e
# machine.ui.error("could not attach device because: #{e}")
raise VagrantPlugins::ProviderLibvirt::Errors::AttachDeviceError,:error_message => e.message
end
end
# TODO once up, mount folders
def enable(machine, folders, _opts)
# Go through each folder and mount
machine.ui.info("mounting p9 share in guest")
# Only mount folders that have a guest path specified.
mount_folders = {}
folders.each do |id, opts|
mount_folders[id] = opts.dup if opts[:guestpath]
end
common_opts = {
:version => '9p2000.L',
}
# Mount the actual folder
machine.guest.capability(
:mount_p9_shared_folder, mount_folders, common_opts)
end
def cleanup(machine, opts)
# driver(machine).clear_shared_folders if machine.id && machine.id != ""
end
end
end
end

View File

@ -6,7 +6,7 @@ end
# This is a sanity check to make sure no one is attempting to install # This is a sanity check to make sure no one is attempting to install
# this into an early Vagrant version. # this into an early Vagrant version.
if Vagrant::VERSION < '1.4.0' if Vagrant::VERSION < '1.5.0'
raise 'The Vagrant Libvirt plugin is only compatible with Vagrant 1.4+' raise 'The Vagrant Libvirt plugin is only compatible with Vagrant 1.4+'
end end
@ -31,6 +31,17 @@ module VagrantPlugins
require_relative 'provider' require_relative 'provider'
Provider Provider
end end
guest_capability("linux", "mount_p9_shared_folder") do
require_relative "cap/mount_p9"
Cap::MountP9
end
# We set p9 as high priority (default 10)
synced_folder("9p", 20) do
require_relative "cap/synced_folder"
VagrantPlugins::SyncedFolder9p::SyncedFolder
end
# This initializes the internationalization strings. # This initializes the internationalization strings.
def self.setup_i18n def self.setup_i18n

View File

@ -0,0 +1,8 @@
<filesystem type='mount' accessmode='<%= accessmode %>'>
<driver type='path' wrpolicy='immediate'/>
<source dir='<%= hostpath %>'/>
<target dir='<%= guestpath %>'/>
<% unless readonly.nil? %>
<readonly />
<% end %>
</filesystem>

View File

@ -1,17 +1,21 @@
require 'erb' require 'erubis'
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
module Util module Util
module ErbTemplate module ErbTemplate
# Taken from fog source.
def to_xml template_name = nil # TODO might be a chance to use vagrant template system according to https://github.com/mitchellh/vagrant/issues/3231
def to_xml template_name = nil, data = binding
erb = template_name || self.class.to_s.split("::").last.downcase erb = template_name || self.class.to_s.split("::").last.downcase
path = File.join(File.dirname(__FILE__), "..", "templates", path = File.join(File.dirname(__FILE__), "..", "templates",
"#{erb}.xml.erb") "#{erb}.xml.erb")
template = File.read(path) template = File.read(path)
ERB.new(template, nil, '-').result(binding)
# TODO according to erubis documentation, we should rather use evaluate and forget about
# binding since the template may then change variables values
Erubis::Eruby.new(template, :trim => true).result(data)
end end
end end