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"
end
group :plugins do
gem "vagrant-libvirt", :path => '.'
end

View File

@ -27,19 +27,26 @@ module VagrantPlugins
b2.use CreateNetworks
b2.use CreateNetworkInterfaces
b2.use StartDomain
b2.use WaitTillUp
b2.use PrepareNFSValidIds
b2.use SyncedFolderCleanup
b2.use SyncedFolders
b2.use StartDomain
b2.use WaitTillUp
b2.use StartDomain
b2.use WaitTillUp
b2.use ForwardPorts
b2.use PrepareNFSSettings
# b2.use PrepareNFSSettings
b2.use ShareFolders
b2.use SetHostname
b2.use SyncFolders
# b2.use SyncFolders
else
b2.use action_start
end
@ -70,6 +77,11 @@ module VagrantPlugins
# Ensure networks are created and active
b3.use CreateNetworks
b3.use PrepareNFSValidIds
b3.use SyncedFolderCleanup
b3.use SyncedFolders
# Start it..
b3.use StartDomain
@ -77,12 +89,9 @@ module VagrantPlugins
# so wait for dhcp lease and store IP into machines data_dir.
b3.use WaitTillUp
b3.use PrepareNFSValidIds
b3.use SyncedFolderCleanup
b3.use SyncedFolders
b3.use ForwardPorts
b3.use PrepareNFSSettings
# b3.use PrepareNFSSettings
b3.use ShareFolders
end
@ -147,7 +156,7 @@ module VagrantPlugins
b2.use ConnectLibvirt
b2.use ClearForwardedPorts
b2.use PruneNFSExports
# b2.use PruneNFSExports
b2.use DestroyDomain
b2.use DestroyNetworks
end
@ -195,7 +204,7 @@ module VagrantPlugins
end
b3.use Provision
b3.use SyncFolders
# b3.use SyncFolders
end
end
end
@ -312,18 +321,25 @@ module VagrantPlugins
autoload :MessageNotCreated, action_root.join('message_not_created')
autoload :MessageNotRunning, action_root.join('message_not_running')
autoload :MessageNotSuspended, action_root.join('message_not_suspended')
autoload :PrepareNFSSettings, action_root.join('prepare_nfs_settings')
autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
autoload :PruneNFSExports, action_root.join('prune_nfs_exports')
autoload :ReadSSHInfo, action_root.join('read_ssh_info')
autoload :ReadState, action_root.join('read_state')
autoload :ResumeDomain, action_root.join('resume_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 :StartDomain, action_root.join('start_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 :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
autoload :SSHRun, 'vagrant/action/builtin/ssh_run'
autoload :HandleBoxUrl, 'vagrant/action/builtin/handle_box_url'
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 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+'
end
@ -31,6 +31,17 @@ module VagrantPlugins
require_relative 'provider'
Provider
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.
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 ProviderLibvirt
module Util
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
path = File.join(File.dirname(__FILE__), "..", "templates",
"#{erb}.xml.erb")
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