synced_folders/nfs: begin implementation (not functional)

This commit is contained in:
Mitchell Hashimoto 2013-11-22 16:51:55 -08:00
parent 9d3818a380
commit 52fe93cc41
6 changed files with 121 additions and 141 deletions

View File

@ -1,128 +0,0 @@
require 'fileutils'
require 'pathname'
require 'zlib'
require "log4r"
require 'vagrant/util/platform'
module Vagrant
module Action
module Builtin
# This built-in middleware exports and mounts NFS shared folders.
#
# To use this middleware, two configuration parameters must be given
# via the environment hash:
#
# - `:nfs_host_ip` - The IP of where to mount the NFS folder from.
# - `:nfs_machine_ip` - The IP of the machine where the NFS folder
# will be mounted.
#
class NFS
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::nfs")
end
def call(env)
# We forward things along first. We do everything on the tail
# end of the middleware call.
@app.call(env)
# Used by prepare_permission, so we need to save it
@env = env
folders = {}
env[:machine].config.vm.synced_folders.each do |id, opts|
# If this synced folder doesn't enable NFS, ignore it.
next if !opts[:nfs]
# Expand the host path, create it if we have to and
# store away the folder.
hostpath = Pathname.new(opts[:hostpath]).
expand_path(env[:root_path])
if !hostpath.directory? && opts[:create]
# Host path doesn't exist, so let's create it.
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
begin
FileUtils.mkpath(hostpath)
rescue Errno::EACCES
raise Vagrant::Errors::SharedFolderCreateFailed,
:path => hostpath.to_s
end
end
# Determine the real path by expanding symlinks and getting
# proper casing. We have to do this after creating it
# if it doesn't exist.
hostpath = hostpath.realpath
hostpath = Util::Platform.fs_real_path(hostpath)
# Set the hostpath back on the options and save it
opts[:hostpath] = hostpath.to_s
folders[id] = opts
end
if !folders.empty?
raise Errors::NFSNoHostIP if !env[:nfs_host_ip]
raise Errors::NFSNoGuestIP if !env[:nfs_machine_ip]
machine_ip = env[:nfs_machine_ip]
machine_ip = [machine_ip] if !machine_ip.is_a?(Array)
# Prepare the folder, this means setting up various options
# and such on the folder itself.
folders.each { |id, opts| prepare_folder(opts) }
# Export the folders
env[:ui].info I18n.t("vagrant.actions.vm.nfs.exporting")
env[:host].nfs_export(env[:machine].id, machine_ip, folders)
# Mount
env[:ui].info I18n.t("vagrant.actions.vm.nfs.mounting")
# 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
# Mount them!
env[:machine].guest.capability(
:mount_nfs_folder, env[:nfs_host_ip], mount_folders)
end
end
protected
def prepare_folder(opts)
opts[:map_uid] = prepare_permission(:uid, opts)
opts[:map_gid] = prepare_permission(:gid, opts)
opts[:nfs_version] ||= 3
# We use a CRC32 to generate a 32-bit checksum so that the
# fsid is compatible with both old and new kernels.
opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s
end
# Prepares the UID/GID settings for a single folder.
def prepare_permission(perm, opts)
key = "map_#{perm}".to_sym
return nil if opts.has_key?(key) && opts[key].nil?
# The options on the hash get priority, then the default
# values
value = opts.has_key?(key) ? opts[key] : @env[:machine].config.nfs.send(key)
return value if value != :auto
# Get UID/GID from folder if we've made it this far
# (value == :auto)
stat = File.stat(opts[:hostpath])
return stat.send(perm)
end
end
end
end
end

View File

@ -1,5 +1,7 @@
require "log4r" require "log4r"
require 'vagrant/util/platform'
module Vagrant module Vagrant
module Action module Action
module Builtin module Builtin
@ -29,15 +31,20 @@ module Vagrant
fs.each do |id, data| fs.each do |id, data|
data[:hostpath] = File.expand_path(data[:hostpath], env[:root_path]) data[:hostpath] = File.expand_path(data[:hostpath], env[:root_path])
# Don't do anything else if this directory exists or its # Create the hostpath if it doesn't exist and we've been told to
# not flagged to auto-create if !File.directory?(data[:hostpath]) && data[:create]
next if File.directory?(data[:hostpath]) || !data[:create] @logger.info("Creating shared folder host directory: #{data[:hostpath]}")
@logger.info("Creating shared folder host directory: #{data[:hostpath]}") begin
begin Pathname.new(data[:hostpath]).mkpath
Pathname.new(data[:hostpath]).mkpath rescue Errno::EACCES
rescue Errno::EACCES raise Vagrant::Errors::SharedFolderCreateFailed,
raise Vagrant::Errors::SharedFolderCreateFailed, path: data[:hostpath]
path: data[:hostpath] end
end
if File.directory?(data[:hostpath])
data[:hostpath] = File.realpath(data[:hostpath])
data[:hostpath] = Util::Platform.fs_real_path(data[:hostpath])
end end
end end
end end

View File

@ -6,10 +6,10 @@ module Vagrant
def usable?(machine) def usable?(machine)
end end
def prepare(machine, folders) def prepare(machine, folders, opts)
end end
def enable(machine, folders) def enable(machine, folders, opts)
end end
end end
end end

View File

@ -8,7 +8,7 @@ module VagrantPlugins
machine.provider_name == :virtualbox machine.provider_name == :virtualbox
end end
def prepare(machine, folders) def prepare(machine, folders, _opts)
defs = [] defs = []
folders.each do |id, data| folders.each do |id, data|
hostpath = Vagrant::Util::Platform.cygwin_windows_path(data[:hostpath]) hostpath = Vagrant::Util::Platform.cygwin_windows_path(data[:hostpath])
@ -23,7 +23,7 @@ module VagrantPlugins
driver(machine).share_folders(defs) driver(machine).share_folders(defs)
end end
def enable(machine, folders) def enable(machine, folders, _opts)
# short guestpaths first, so we don't step on ourselves # short guestpaths first, so we don't step on ourselves
folders = folders.sort_by do |id, data| folders = folders.sort_by do |id, data|
if data[:guestpath] if data[:guestpath]

View File

@ -0,0 +1,18 @@
require "vagrant"
module VagrantPlugins
module SyncedFolderNFS
class Plugin < Vagrant.plugin("2")
name "NFS synced folders"
description <<-EOF
The NFS synced folders plugin enables you to use NFS as a synced folder
implementation.
EOF
synced_folder(:nfs, 5) do
require File.expand_path("../synced_folder", __FILE__)
SyncedFolder
end
end
end
end

View File

@ -0,0 +1,83 @@
require 'fileutils'
require 'zlib'
require "vagrant/util/platform"
module VagrantPlugins
module SyncedFolderNFS
# This synced folder requires that two keys be set on the environment
# within the middleware sequence:
#
# - `:nfs_host_ip` - The IP of where to mount the NFS folder from.
# - `:nfs_machine_ip` - The IP of the machine where the NFS folder
# will be mounted.
#
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
def usable?(machine)
# NFS is always available
true
end
def prepare(machine, folders, opts)
# Nothing is necessary to do before VM boot.
end
def enable(machine, folders, nfsopts)
raise Errors::NFSNoHostIP if !nfsopts[:nfs_host_ip]
raise Errors::NFSNoGuestIP if !nfsopts[:nfs_machine_ip]
machine_ip = nfsopts[:nfs_machine_ip]
machine_ip = [machine_ip] if !machine_ip.is_a?(Array)
# Prepare the folder, this means setting up various options
# and such on the folder itself.
folders.each { |id, opts| prepare_folder(machine, opts) }
# Export the folders
machine.ui.info I18n.t("vagrant.actions.vm.nfs.exporting")
machine.env.host.nfs_export(machine.id, machine_ip, folders)
# Mount
machine.ui.info I18n.t("vagrant.actions.vm.nfs.mounting")
# 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
# Mount them!
machine.guest.capability(
:mount_nfs_folder, nfsopts[:nfs_host_ip], mount_folders)
end
protected
def prepare_folder(machine, opts)
opts[:map_uid] = prepare_permission(machine, :uid, opts)
opts[:map_gid] = prepare_permission(machine, :gid, opts)
opts[:nfs_version] ||= 3
# We use a CRC32 to generate a 32-bit checksum so that the
# fsid is compatible with both old and new kernels.
opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s
end
# Prepares the UID/GID settings for a single folder.
def prepare_permission(machine, perm, opts)
key = "map_#{perm}".to_sym
return nil if opts.has_key?(key) && opts[key].nil?
# The options on the hash get priority, then the default
# values
value = opts.has_key?(key) ? opts[key] : machine.config.nfs.send(key)
return value if value != :auto
# Get UID/GID from folder if we've made it this far
# (value == :auto)
stat = File.stat(opts[:hostpath])
return stat.send(perm)
end
end
end
end