synced_folders/smb: basically working
This commit is contained in:
parent
31abc3f4a3
commit
0fe4a4af26
|
@ -0,0 +1,20 @@
|
|||
module VagrantPlugins
|
||||
module GuestLinux
|
||||
module Cap
|
||||
module ChooseAddressableIPAddr
|
||||
def self.choose_addressable_ip_addr(machine, possible)
|
||||
machine.communicate.tap do |comm|
|
||||
possible.each do |ip|
|
||||
command = "ping -c1 -w1 -W1 #{ip}"
|
||||
if comm.test(command)
|
||||
return ip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
module VagrantPlugins
|
||||
module GuestLinux
|
||||
module Cap
|
||||
class MountSMBSharedFolder
|
||||
def self.mount_smb_shared_folder(machine, name, guestpath, options)
|
||||
expanded_guest_path = machine.guest.capability(
|
||||
:shell_expand_guest_path, guestpath)
|
||||
|
||||
mount_commands = []
|
||||
mount_device = "//#{options[:smb_host]}/#{name}"
|
||||
|
||||
if options[:owner].is_a? Integer
|
||||
mount_uid = options[:owner]
|
||||
else
|
||||
mount_uid = "`id -u #{options[:owner]}`"
|
||||
end
|
||||
|
||||
if options[:group].is_a? Integer
|
||||
mount_gid = options[:group]
|
||||
mount_gid_old = options[:group]
|
||||
else
|
||||
mount_gid = "`getent group #{options[:group]} | cut -d: -f3`"
|
||||
mount_gid_old = "`id -g #{options[:group]}`"
|
||||
end
|
||||
|
||||
options[:mount_options] ||= []
|
||||
options[:mount_options] << "sec=ntlm"
|
||||
options[:mount_options] << "username=#{options[:smb_username]}"
|
||||
options[:mount_options] << "pass=#{options[:smb_password]}"
|
||||
|
||||
# First mount command uses getent to get the group
|
||||
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
|
||||
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
||||
mount_commands << "mount -t cifs #{mount_options} #{mount_device} #{expanded_guest_path}"
|
||||
|
||||
# Second mount command uses the old style `id -g`
|
||||
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid_old}"
|
||||
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
||||
mount_commands << "mount -t cifs #{mount_options} #{mount_device} #{expanded_guest_path}"
|
||||
|
||||
# Create the guest path if it doesn't exist
|
||||
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
|
||||
|
||||
# Attempt to mount the folder. We retry here a few times because
|
||||
# it can fail early on.
|
||||
attempts = 0
|
||||
while true
|
||||
success = true
|
||||
|
||||
mount_commands.each do |command|
|
||||
no_such_device = false
|
||||
status = machine.communicate.sudo(command, error_check: false) do |type, data|
|
||||
no_such_device = true if type == :stderr && data =~ /No such device/i
|
||||
end
|
||||
|
||||
success = status == 0 && !no_such_device
|
||||
break if success
|
||||
end
|
||||
|
||||
break if success
|
||||
|
||||
attempts += 1
|
||||
if attempts > 10
|
||||
raise Vagrant::Errors::LinuxMountFailed,
|
||||
command: mount_commands.join("\n")
|
||||
end
|
||||
|
||||
sleep 2
|
||||
end
|
||||
|
||||
# Emit an upstart event if we can
|
||||
if machine.communicate.test("test -x /sbin/initctl")
|
||||
machine.communicate.sudo(
|
||||
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,6 +11,11 @@ module VagrantPlugins
|
|||
Guest
|
||||
end
|
||||
|
||||
guest_capability("linux", "choose_addressable_ip_addr") do
|
||||
require_relative "cap/choose_addressable_ip_addr"
|
||||
Cap::ChooseAddressableIPAddr
|
||||
end
|
||||
|
||||
guest_capability("linux", "halt") do
|
||||
require_relative "cap/halt"
|
||||
Cap::Halt
|
||||
|
@ -31,6 +36,11 @@ module VagrantPlugins
|
|||
Cap::MountNFS
|
||||
end
|
||||
|
||||
guest_capability("linux", "mount_smb_shared_folder") do
|
||||
require_relative "cap/mount_smb_shared_folder"
|
||||
Cap::MountSMBSharedFolder
|
||||
end
|
||||
|
||||
guest_capability("linux", "mount_virtualbox_shared_folder") do
|
||||
require_relative "cap/mount_virtualbox_shared_folder"
|
||||
Cap::MountVirtualBoxSharedFolder
|
||||
|
|
|
@ -16,14 +16,9 @@ module VagrantPlugins
|
|||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use action_halt
|
||||
b2.use Call, WaitForState, :off, 120 do |env2, b3|
|
||||
if env2[:result]
|
||||
b3.use action_up
|
||||
else
|
||||
env2[:ui].info("Machine did not reload, Check machine's status")
|
||||
end
|
||||
end
|
||||
b2.use action_start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -58,7 +53,12 @@ module VagrantPlugins
|
|||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
b2.use StopInstance
|
||||
|
||||
b2.use Call, GracefulHalt, :off, :running do |env2, b3|
|
||||
if !env2[:result]
|
||||
b3.use StopInstance
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -69,6 +69,8 @@ module VagrantPlugins
|
|||
b.use StartInstance
|
||||
b.use WaitForIPAddress
|
||||
b.use WaitForCommunicator, [:running]
|
||||
b.use SyncedFolders
|
||||
|
||||
#b.use ShareFolders
|
||||
#b.use SyncFolders
|
||||
end
|
||||
|
@ -79,18 +81,11 @@ module VagrantPlugins
|
|||
b.use HandleBox
|
||||
b.use ConfigValidate
|
||||
b.use Call, IsCreated do |env1, b1|
|
||||
if env1[:result]
|
||||
b1.use Call, IsStopped do |env2, b2|
|
||||
if env2[:result]
|
||||
b2.use action_start
|
||||
else
|
||||
b2.use MessageAlreadyCreated
|
||||
end
|
||||
end
|
||||
else
|
||||
if !env1[:result]
|
||||
b1.use Import
|
||||
b1.use action_start
|
||||
end
|
||||
|
||||
b1.use action_start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -145,7 +140,6 @@ module VagrantPlugins
|
|||
autoload :ReadGuestIP, action_root.join('read_guest_ip')
|
||||
autoload :ShareFolders, action_root.join('share_folders')
|
||||
autoload :WaitForIPAddress, action_root.join("wait_for_ip_address")
|
||||
autoload :WaitForState, action_root.join('wait_for_state')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info("Stopping the machine..."))
|
||||
env[:ui].info("Stopping the machine...")
|
||||
options = { VmId: env[:machine].id }
|
||||
env[:machine].provider.driver.execute('stop_vm.ps1', options)
|
||||
@app.call(env)
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Open Technologies, Inc.
|
||||
# All Rights Reserved. Licensed under the MIT License.
|
||||
#--------------------------------------------------------------------------
|
||||
require "log4r"
|
||||
require "timeout"
|
||||
require "debugger"
|
||||
|
||||
module VagrantPlugins
|
||||
module HyperV
|
||||
module Action
|
||||
class WaitForState
|
||||
def initialize(app, env, state, timeout)
|
||||
@app = app
|
||||
@state = state
|
||||
@timeout = timeout
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:result] = true
|
||||
# Wait until the Machine's state is disabled (ie State of Halt)
|
||||
unless env[:machine].state.id == @state
|
||||
env[:ui].info("Waiting for machine to #{@state}")
|
||||
begin
|
||||
Timeout.timeout(@timeout) do
|
||||
until env[:machine].state.id == @state
|
||||
sleep 2
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
env[:result] = false # couldn't reach state in time
|
||||
end
|
||||
end
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +1,30 @@
|
|||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Open Technologies, Inc.
|
||||
# All Rights Reserved. Licensed under the MIT License.
|
||||
#--------------------------------------------------------------------------
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$path,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$share_name,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$host_share_username
|
||||
)
|
||||
|
||||
param (
|
||||
[string]$path = $(throw "-path is required."),
|
||||
[string]$share_name = $(throw "-share_name is required."),
|
||||
[string]$host_share_username = $(throw "-host_share_username is required")
|
||||
)
|
||||
$ErrorAction = "Stop"
|
||||
|
||||
# Include the following modules
|
||||
$presentDir = Split-Path -parent $PSCommandPath
|
||||
$modules = @()
|
||||
$modules += $presentDir + "\utils\write_messages.ps1"
|
||||
forEach ($module in $modules) { . $module }
|
||||
|
||||
try {
|
||||
# See all available shares and check alert user for existing / conflicting share name
|
||||
$shared_folders = net share
|
||||
$reg = "$share_name(\s+)$path(\s)"
|
||||
$existing_share = $shared_folders -Match $reg
|
||||
if ($existing_share) {
|
||||
# See all available shares and check alert user for
|
||||
# existing/conflicting share name
|
||||
$shared_folders = net share
|
||||
$reg = "$share_name(\s+)$path(\s)"
|
||||
$existing_share = $shared_folders -Match $reg
|
||||
if ($existing_share) {
|
||||
# Always clear the existing share name and create a new one
|
||||
net share $share_name /delete /y
|
||||
}
|
||||
|
||||
$computer_name = $(Get-WmiObject Win32_Computersystem).name
|
||||
$grant_permission = "$computer_name\$host_share_username,Full"
|
||||
$result = net share $share_name=$path /unlimited /GRANT:$grant_permission
|
||||
if ($result -Match "$share_name was shared successfully.") {
|
||||
$resultHash = @{
|
||||
message = "OK"
|
||||
}
|
||||
$result = ConvertTo-Json $resultHash
|
||||
Write-Output-Message $result
|
||||
} else {
|
||||
$reg = "^$share_name(\s+)"
|
||||
$existing_share = $shared_folders -Match $reg
|
||||
Write-Error-Message "IGNORING Conflicting share name, A share name already exist $existing_share"
|
||||
}
|
||||
} catch {
|
||||
Write-Error-Message $_
|
||||
return
|
||||
}
|
||||
|
||||
$computer_name = $(Get-WmiObject Win32_Computersystem).name
|
||||
$grant_permission = "$computer_name\$host_share_username,Full"
|
||||
$result = net share $share_name=$path /unlimited /GRANT:$grant_permission
|
||||
if ($result -Match "$share_name was shared successfully.") {
|
||||
exit 0
|
||||
}
|
||||
|
||||
$host.ui.WriteErrorLine("Error: $result")
|
||||
exit 1
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
module VagrantPlugins
|
||||
module SyncedFolderSMB
|
||||
module Errors
|
||||
# A convenient superclass for all our errors.
|
||||
class SMBError < Vagrant::Errors::VagrantError
|
||||
error_namespace("vagrant_sf_smb.errors")
|
||||
end
|
||||
|
||||
class DefineShareFailed < SMBError
|
||||
error_key(:define_share_failed)
|
||||
end
|
||||
|
||||
class NoHostIPAddr < SMBError
|
||||
error_key(:no_routable_host_addr)
|
||||
end
|
||||
|
||||
class PowershellError < SMBError
|
||||
error_key(:powershell_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module SyncedFolderSMB
|
||||
autoload :Errors, File.expand_path("../errors", __FILE__)
|
||||
|
||||
# This plugin implements SMB synced folders.
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "SMB synced folders"
|
||||
description <<-EOF
|
||||
The SMB synced folders plugin enables you to use SMB folders on
|
||||
Windows and share them to guest machines.
|
||||
EOF
|
||||
|
||||
synced_folder("smb", 5) do
|
||||
require_relative "synced_folder"
|
||||
init!
|
||||
SyncedFolder
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.init!
|
||||
return if defined?(@_init)
|
||||
I18n.load_path << File.expand_path(
|
||||
"templates/locales/synced_folder_smb.yml", Vagrant.source_root)
|
||||
I18n.reload!
|
||||
@_init = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
$ErrorAction = "Stop"
|
||||
|
||||
$net = Get-WmiObject -class win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"'
|
||||
$result = @{
|
||||
ip_addresses = $net.ipaddress
|
||||
}
|
||||
|
||||
Write-Output $(ConvertTo-Json $result)
|
|
@ -0,0 +1,34 @@
|
|||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$path,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$share_name,
|
||||
[string]$host_share_username = $null
|
||||
)
|
||||
|
||||
$ErrorAction = "Stop"
|
||||
|
||||
# See all available shares and check alert user for existing/conflicting
|
||||
# share names.
|
||||
$path_regexp = [System.Text.RegularExpressions.Regex]::Escape($path)
|
||||
$name_regexp = [System.Text.RegularExpressions.Regex]::Escape($share_name)
|
||||
$reg = "(?m)$name_regexp\s+$path_regexp\s"
|
||||
$existing_share = $($(net share) -join "`n") -Match $reg
|
||||
if ($existing_share) {
|
||||
# Always clear the existing share name and create a new one
|
||||
net share $share_name /delete /y
|
||||
}
|
||||
|
||||
$grant = "Everyone,Full"
|
||||
if (![string]::IsNullOrEmpty($host_share_username)) {
|
||||
$computer_name = $(Get-WmiObject Win32_Computersystem).name
|
||||
$grant = "$computer_name\$host_share_username,Full"
|
||||
}
|
||||
|
||||
$result = net share $share_name=$path /unlimited /GRANT:$grant
|
||||
if ($result -Match "$share_name was shared successfully.") {
|
||||
exit 0
|
||||
}
|
||||
|
||||
$host.ui.WriteErrorLine("Error: $result")
|
||||
exit 1
|
|
@ -0,0 +1,138 @@
|
|||
require "json"
|
||||
|
||||
require "log4r"
|
||||
|
||||
require "vagrant/util/platform"
|
||||
require "vagrant/util/powershell"
|
||||
|
||||
module VagrantPlugins
|
||||
module SyncedFolderSMB
|
||||
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::synced_folders::smb")
|
||||
end
|
||||
|
||||
def usable?(machine, raise_error=false)
|
||||
if !Vagrant::Util::Platform.windows?
|
||||
# TODO: raise error if specified
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def prepare(machine, folders, opts)
|
||||
script_path = File.expand_path("../scripts/set_share.ps1", __FILE__)
|
||||
|
||||
folders.each do |id, data|
|
||||
hostpath = data[:hostpath]
|
||||
|
||||
data[:smb_id] ||= "#{machine.id}-#{id.gsub("/", "-")}"
|
||||
|
||||
args = []
|
||||
args << "-path" << hostpath.gsub("/", "\\")
|
||||
args << "-share_name" << data[:smb_id]
|
||||
#args << "-host_share_username" << "mitchellh"
|
||||
|
||||
r = Vagrant::Util::PowerShell.execute(script_path, *args)
|
||||
if r.exit_code != 0
|
||||
raise Errors::DefineShareFailed,
|
||||
host: hostpath.to_s,
|
||||
stderr: r.stderr,
|
||||
stdout: r.stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def enable(machine, folders, nfsopts)
|
||||
machine.ui.output(I18n.t("vagrant_sf_smb.mounting"))
|
||||
|
||||
# Make sure that this machine knows this dance
|
||||
if !machine.guest.capability?(:mount_smb_shared_folder)
|
||||
raise Vagrant::Errors::GuestCapabilityNotFound,
|
||||
cap: "mount_smb_shared_folder",
|
||||
guest: machine.guest.name.to_s
|
||||
end
|
||||
|
||||
# Detect the host IP for this guest if one wasn't specified
|
||||
# for every folder.
|
||||
host_ip = nil
|
||||
need_host_ip = false
|
||||
folders.each do |id, data|
|
||||
if !data[:smb_host]
|
||||
need_host_ip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if need_host_ip
|
||||
candidate_ips = load_host_ips
|
||||
@logger.debug("Potential host IPs: #{candidate_ips.inspect}")
|
||||
host_ip = machine.guest.capability(
|
||||
:choose_addressable_ip_addr, candidate_ips)
|
||||
if !host_ip
|
||||
raise Errors::NoHostIPAddr
|
||||
end
|
||||
end
|
||||
|
||||
# If we need auth information, then ask the user
|
||||
username = nil
|
||||
password = nil
|
||||
need_auth = false
|
||||
folders.each do |id, data|
|
||||
if !data[:smb_username] || !data[:smb_password]
|
||||
need_auth = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if need_auth
|
||||
machine.ui.detail(I18n.t("vagrant_sf_smb.warning_password") + "\n ")
|
||||
username = machine.ui.ask("Username: ")
|
||||
password = machine.ui.ask("Password (will be hidden): ", echo: false)
|
||||
end
|
||||
|
||||
# This is used for defaulting the owner/group
|
||||
ssh_info = machine.ssh_info
|
||||
|
||||
folders.each do |id, data|
|
||||
data = data.dup
|
||||
data[:smb_host] ||= host_ip
|
||||
data[:smb_username] ||= username
|
||||
data[:smb_password] ||= password
|
||||
|
||||
# Default the owner/group of the folder to the SSH user
|
||||
data[:owner] ||= ssh_info[:username]
|
||||
data[:group] ||= ssh_info[:username]
|
||||
|
||||
machine.ui.detail(I18n.t(
|
||||
"vagrant_sf_smb.mounting_single",
|
||||
host: data[:hostpath].to_s,
|
||||
guest: data[:guestpath].to_s))
|
||||
machine.guest.capability(
|
||||
:mount_smb_shared_folder, data[:smb_id], data[:guestpath], data)
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup(machine, opts)
|
||||
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def load_host_ips
|
||||
script_path = File.expand_path("../scripts/host_info.ps1", __FILE__)
|
||||
r = Vagrant::Util::PowerShell.execute(script_path)
|
||||
if r.exit_code != 0
|
||||
raise Errors::PowershellError,
|
||||
script: script_path,
|
||||
stderr: r.stderr
|
||||
end
|
||||
|
||||
JSON.parse(r.stdout)["ip_addresses"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
en:
|
||||
vagrant_sf_smb:
|
||||
mounting: |-
|
||||
Mounting SMB shared folders...
|
||||
mounting_single: |-
|
||||
%{host} => %{guest}
|
||||
warning_password: |-
|
||||
You will be asked for the username and password to use to mount the
|
||||
folders shortly. Please use the proper username/password of your
|
||||
Windows account.
|
||||
|
||||
errors:
|
||||
define_share_failed: |-
|
||||
Exporting an SMB share failed! Details about the failure are shown
|
||||
below. Please inspect the error message and correct any problems.
|
||||
|
||||
Host path: %{host}
|
||||
|
||||
Stderr: %{stderr}
|
||||
|
||||
Stdout: %{stdout}
|
||||
no_routable_host_addr: |-
|
||||
We couldn't detect an IP address that was routable to this
|
||||
machine from the guest machine! Please verify networking is properly
|
||||
setup in the guest machine and that it is able to access this
|
||||
host.
|
||||
|
||||
As another option, you can manually specify an IP for the machine
|
||||
to mount from using the `smb_host` option to the synced folder.
|
||||
powershell_error: |-
|
||||
An error occurred while executing a PowerShell script. This error
|
||||
is shown below. Please read the error message and see if this is
|
||||
a configuration error with your system. If it is not, then please
|
||||
report a bug.
|
||||
|
||||
Script: %{script}
|
||||
Error:
|
||||
|
||||
%{stderr}
|
Loading…
Reference in New Issue