vagrant-azure 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/Gemfile +19 -0
- data/LICENSE +4 -0
- data/README.md +9 -0
- data/Rakefile +14 -0
- data/lib/vagrant-azure.rb +24 -0
- data/lib/vagrant-azure/action.rb +249 -0
- data/lib/vagrant-azure/action/connect_azure.rb +41 -0
- data/lib/vagrant-azure/action/provision.rb +40 -0
- data/lib/vagrant-azure/action/rdp.rb +62 -0
- data/lib/vagrant-azure/action/read_ssh_info.rb +51 -0
- data/lib/vagrant-azure/action/read_state.rb +46 -0
- data/lib/vagrant-azure/action/restart_vm.rb +27 -0
- data/lib/vagrant-azure/action/run_instance.rb +111 -0
- data/lib/vagrant-azure/action/start_instance.rb +35 -0
- data/lib/vagrant-azure/action/stop_instance.rb +38 -0
- data/lib/vagrant-azure/action/terminate_instance.rb +34 -0
- data/lib/vagrant-azure/action/wait_for_state.rb +49 -0
- data/lib/vagrant-azure/command/rdp/command.rb +21 -0
- data/lib/vagrant-azure/config.rb +147 -0
- data/lib/vagrant-azure/driver.rb +79 -0
- data/lib/vagrant-azure/plugin.rb +87 -0
- data/lib/vagrant-azure/provider.rb +70 -0
- data/lib/vagrant-azure/provisioner/puppet.rb +109 -0
- data/lib/vagrant-azure/scripts/check_winrm.ps1 +41 -0
- data/lib/vagrant-azure/scripts/export_vm.ps1 +31 -0
- data/lib/vagrant-azure/scripts/file_sync.ps1 +145 -0
- data/lib/vagrant-azure/scripts/host_info.ps1 +25 -0
- data/lib/vagrant-azure/scripts/hyperv_manager.ps1 +36 -0
- data/lib/vagrant-azure/scripts/run_in_remote.ps1 +32 -0
- data/lib/vagrant-azure/scripts/upload_file.ps1 +95 -0
- data/lib/vagrant-azure/scripts/utils/create_session.ps1 +34 -0
- data/lib/vagrant-azure/scripts/utils/write_messages.ps1 +18 -0
- data/lib/vagrant-azure/version.rb +10 -0
- data/locales/en.yml +14 -0
- data/vagrant-azure.gemspec +58 -0
- metadata +167 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
#---------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
require 'json'
|
6
|
+
require "#{Vagrant::source_root}/plugins/providers/hyperv/driver"
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module WinAzure
|
10
|
+
class Driver < VagrantPlugins::HyperV::Driver
|
11
|
+
def initialize(machine)
|
12
|
+
@id = machine.id
|
13
|
+
@machine = machine
|
14
|
+
end
|
15
|
+
|
16
|
+
def ssh_info
|
17
|
+
@ssh_info ||= @machine.provider.winrm_info
|
18
|
+
@ssh_info[:username] ||= @machine.config.ssh.username
|
19
|
+
@ssh_info[:password] ||= @machine.config.ssh.password
|
20
|
+
@ssh_info
|
21
|
+
end
|
22
|
+
|
23
|
+
def remote_credentials
|
24
|
+
@remote_credentials ||= {
|
25
|
+
guest_ip: ssh_info[:host],
|
26
|
+
guest_port: ssh_info[:port],
|
27
|
+
username: ssh_info[:username],
|
28
|
+
password: ssh_info[:password]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_remote_ps(command, &block)
|
33
|
+
options = remote_credentials.merge(command: command)
|
34
|
+
script_path = local_script_path('run_in_remote.ps1')
|
35
|
+
|
36
|
+
ps_options = []
|
37
|
+
|
38
|
+
options.each do |key, value|
|
39
|
+
ps_options << "-#{key}"
|
40
|
+
ps_options << "'#{value}'"
|
41
|
+
end
|
42
|
+
|
43
|
+
ps_options << '-ErrorAction' << 'Stop'
|
44
|
+
opts = { notify: [:stdout, :stderr, :stdin] }
|
45
|
+
Vagrant::Util::PowerShell.execute(
|
46
|
+
script_path,
|
47
|
+
*ps_options,
|
48
|
+
**opts,
|
49
|
+
&block
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def upload(from, to)
|
54
|
+
options = {
|
55
|
+
host_path: windows_path(from),
|
56
|
+
guest_path: windows_path(to)
|
57
|
+
}.merge(remote_credentials)
|
58
|
+
|
59
|
+
script_path = local_script_path('upload_file.ps1')
|
60
|
+
execute(script_path, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def local_script_path(path)
|
66
|
+
lib_path = Pathname.new(File.expand_path('../scripts', __FILE__))
|
67
|
+
windows_path(lib_path.join(path).to_s)
|
68
|
+
end
|
69
|
+
|
70
|
+
def windows_path(path)
|
71
|
+
if path
|
72
|
+
path = path.gsub('/', "\\")
|
73
|
+
path = "c:#{path}" if path =~ /^\\/
|
74
|
+
end
|
75
|
+
path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#--------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
begin
|
6
|
+
require 'vagrant'
|
7
|
+
rescue LoadError
|
8
|
+
raise 'The Vagrant Azure plugin must be run within Vagrant.'
|
9
|
+
end
|
10
|
+
|
11
|
+
# This is a sanity check to make sure no one is attempting to install this into
|
12
|
+
# an early Vagrant version.
|
13
|
+
if Vagrant::VERSION < '1.2.0'
|
14
|
+
raise 'The Vagrant Azure plugin is only compatible with Vagrant 1.2+'
|
15
|
+
end
|
16
|
+
|
17
|
+
module VagrantPlugins
|
18
|
+
module WinAzure
|
19
|
+
class Plugin < Vagrant.plugin('2')
|
20
|
+
name 'azure'
|
21
|
+
description <<-DESC
|
22
|
+
This plugin installs a provider that allows Vagrant to manage
|
23
|
+
machines in Windows Azure.
|
24
|
+
DESC
|
25
|
+
|
26
|
+
config(:azure, :provider) do
|
27
|
+
require_relative 'config'
|
28
|
+
Config
|
29
|
+
end
|
30
|
+
|
31
|
+
provider(:azure, parallel: true) do
|
32
|
+
# Setup logging and i18n
|
33
|
+
setup_logging
|
34
|
+
setup_i18n
|
35
|
+
|
36
|
+
# Return the provider
|
37
|
+
require_relative 'provider'
|
38
|
+
Provider
|
39
|
+
end
|
40
|
+
|
41
|
+
command 'rdp' do
|
42
|
+
require_relative 'command/rdp/command'
|
43
|
+
Command
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.setup_i18n
|
47
|
+
I18n.load_path << File.expand_path(
|
48
|
+
'locales/en.yml',
|
49
|
+
WinAzure.source_root
|
50
|
+
)
|
51
|
+
I18n.load_path << File.expand_path(
|
52
|
+
'templates/locales/providers_hyperv.yml',
|
53
|
+
Vagrant.source_root
|
54
|
+
)
|
55
|
+
I18n.reload!
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.setup_logging
|
59
|
+
require 'log4r'
|
60
|
+
|
61
|
+
level = nil
|
62
|
+
begin
|
63
|
+
level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase)
|
64
|
+
rescue NameError
|
65
|
+
# This means that the logging constant wasn't found,
|
66
|
+
# which is fine. We just keep `level` as `nil`. But
|
67
|
+
# we tell the user.
|
68
|
+
level = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Some constants, such as "true" resolve to booleans, so the
|
72
|
+
# above error checking doesn't catch it. This will check to make
|
73
|
+
# sure that the log level is an integer, as Log4r requires.
|
74
|
+
level = nil if !level.is_a?(Integer)
|
75
|
+
|
76
|
+
# Set the logging level on all "vagrant" namespaced logs as long as
|
77
|
+
# we have a valid level
|
78
|
+
if level
|
79
|
+
logger = Log4r::Logger.new("vagrant_azure")
|
80
|
+
logger.outputters = Log4r::Outputter.stderr
|
81
|
+
logger.level = level
|
82
|
+
logger = nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#--------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
require 'log4r'
|
6
|
+
require 'vagrant'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module WinAzure
|
10
|
+
class Provider < Vagrant.plugin('2', :provider)
|
11
|
+
attr_reader :driver
|
12
|
+
|
13
|
+
def initialize(machine)
|
14
|
+
@machine = machine
|
15
|
+
|
16
|
+
# Load the driver
|
17
|
+
machine_id_changed
|
18
|
+
end
|
19
|
+
|
20
|
+
def action(name)
|
21
|
+
# Attempt to get the action method from the Action class if it
|
22
|
+
# exists, otherwise return nil to show that we don't support the
|
23
|
+
# given action.
|
24
|
+
action_method = "action_#{name}"
|
25
|
+
return Action.send(action_method) if Action.respond_to?(action_method)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def machine_id_changed
|
30
|
+
@driver = Driver.new(@machine)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssh_info
|
34
|
+
# Run a custom action called "read_ssh_info" which does what it
|
35
|
+
# says and puts the resulting SSH info into the `:machine_ssh_info`
|
36
|
+
# key in the environment.
|
37
|
+
env = @machine.action('read_ssh_info')
|
38
|
+
env[:machine_ssh_info]
|
39
|
+
end
|
40
|
+
|
41
|
+
def rdp_info
|
42
|
+
env = @machine.action('read_rdp_info')
|
43
|
+
env[:machine_ssh_info]
|
44
|
+
end
|
45
|
+
|
46
|
+
def winrm_info
|
47
|
+
env = @machine.action('read_winrm_info')
|
48
|
+
env[:machine_ssh_info]
|
49
|
+
end
|
50
|
+
|
51
|
+
def state
|
52
|
+
# Run a custom action we define called "read_state" which does what it
|
53
|
+
# says. It puts the state in the `:machine_state_id` key in the env
|
54
|
+
env = @machine.action('read_state')
|
55
|
+
state_id = env[:machine_state_id]
|
56
|
+
|
57
|
+
short = "Machine's current state is #{state_id}"
|
58
|
+
long = ""
|
59
|
+
|
60
|
+
# Return the MachineState object
|
61
|
+
Vagrant::MachineState.new(state_id, short, long)
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
id = @machine.id.nil? ? 'new' : @machine.id
|
66
|
+
"Azure (#{id})"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#---------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#---------------------------------------------------------------------------
|
5
|
+
require 'fileutils'
|
6
|
+
require 'tempfile'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module WinAzure
|
10
|
+
module Provisioner
|
11
|
+
class Puppet
|
12
|
+
attr_reader :provisioner
|
13
|
+
|
14
|
+
def initialize(env)
|
15
|
+
@env = env
|
16
|
+
@provisioner = env[:provisioner]
|
17
|
+
end
|
18
|
+
|
19
|
+
def provision_for_windows
|
20
|
+
options = [config.options].flatten
|
21
|
+
@module_paths = provisioner.instance_variable_get('@module_paths')
|
22
|
+
@hiera_config_path = provisioner.instance_variable_get(
|
23
|
+
'@hiera_config_path'
|
24
|
+
)
|
25
|
+
@manifest_file = provisioner.instance_variable_get('@manifest_file')
|
26
|
+
|
27
|
+
# Copy the manifests directory to the guest
|
28
|
+
if config.manifests_path[0].to_sym == :host
|
29
|
+
@env[:machine].provider.driver.upload(
|
30
|
+
File.expand_path(
|
31
|
+
config.manifests_path[1], @env[:machine].env.root_path
|
32
|
+
),
|
33
|
+
provisioner.manifests_guest_path
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Copy the module paths to the guest
|
38
|
+
@module_paths.each do |from, to|
|
39
|
+
@env[:machine].provider.driver.upload(from.to_s, to)
|
40
|
+
end
|
41
|
+
|
42
|
+
module_paths = @module_paths.map { |_, to| to }
|
43
|
+
unless module_paths.empty?
|
44
|
+
# Prepend the default module path
|
45
|
+
module_paths.unshift('/ProgramData/PuppetLabs/puppet/etc/modules')
|
46
|
+
|
47
|
+
# Add the command line switch to add the module path
|
48
|
+
options << "--modulepath \"#{module_paths.join(':')}\""
|
49
|
+
end
|
50
|
+
|
51
|
+
if @hiera_config_path
|
52
|
+
options << "--hiera_config=#{@hiera_config_path}"
|
53
|
+
|
54
|
+
# Upload Hiera configuration if we have it
|
55
|
+
local_hiera_path = File.expand_path(
|
56
|
+
config.hiera_config_path,
|
57
|
+
@env[:machine].env.root_path
|
58
|
+
)
|
59
|
+
|
60
|
+
@env[:machine].provider.driver.upload(
|
61
|
+
local_hiera_path,
|
62
|
+
@hiera_config_path
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
options << "--manifestdir #{provisioner.manifests_guest_path}"
|
67
|
+
options << "--detailed-exitcodes"
|
68
|
+
options << @manifest_file
|
69
|
+
options = options.join(' ')
|
70
|
+
|
71
|
+
# Build up the custome facts if we have any
|
72
|
+
facter = ''
|
73
|
+
unless config.facter.empty?
|
74
|
+
facts = []
|
75
|
+
config.facter.each do |key, value|
|
76
|
+
facts << "FACTER_#{key}='#{value}'"
|
77
|
+
end
|
78
|
+
|
79
|
+
facter = "#{facts.join(' ')}"
|
80
|
+
end
|
81
|
+
|
82
|
+
command = "#{facter}puppet apply #{options}"
|
83
|
+
|
84
|
+
if config.working_directory
|
85
|
+
command = "cd #{config.working_directory} && #{command}"
|
86
|
+
end
|
87
|
+
|
88
|
+
@env[:ui].info I18n.t(
|
89
|
+
'vagrant_azure.provisioners.puppet.running_puppet',
|
90
|
+
manifest: config.manifest_file
|
91
|
+
)
|
92
|
+
@env[:ui].info 'Executing puppet script in Windows Azure VM'
|
93
|
+
@env[:machine].provider.driver.run_remote_ps(command) do |type, data|
|
94
|
+
# Output the data with the proper color based on the stream.
|
95
|
+
if (type == :stdout || type == :stderr)
|
96
|
+
@env[:ui].detail data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def config
|
104
|
+
provisioner.config
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#-------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
param (
|
6
|
+
[string]$guest_ip = $(throw "-guest_ip is required."),
|
7
|
+
[string]$username = $(throw "-guest_username is required."),
|
8
|
+
[string]$password = $(throw "-guest_password is required.")
|
9
|
+
)
|
10
|
+
|
11
|
+
# Include the following modules
|
12
|
+
$presentDir = Split-Path -parent $PSCommandPath
|
13
|
+
. ([System.IO.Path]::Combine($presentDir, "utils\write_messages.ps1"))
|
14
|
+
. ([System.IO.Path]::Combine($presentDir, "utils\create_session.ps1"))
|
15
|
+
|
16
|
+
try {
|
17
|
+
$response = Create-Remote-Session $guest_ip $username $password
|
18
|
+
if (!$response["session"] -and $response["error"]) {
|
19
|
+
Write-Host $response["error"]
|
20
|
+
return
|
21
|
+
}
|
22
|
+
function Remote-Execute() {
|
23
|
+
$winrm_state = ""
|
24
|
+
get-service winrm | ForEach-Object {
|
25
|
+
$winrm_state = $_.status
|
26
|
+
}
|
27
|
+
return "$winrm_state"
|
28
|
+
}
|
29
|
+
$result = Invoke-Command -Session $response["session"] -ScriptBlock ${function:Remote-Execute} -ErrorAction "stop"
|
30
|
+
$resultHash = @{
|
31
|
+
message = "$result"
|
32
|
+
}
|
33
|
+
Write-Output-Message $resultHash
|
34
|
+
} catch {
|
35
|
+
$errortHash = @{
|
36
|
+
type = "PowerShellError"
|
37
|
+
error ="Failed to copy file $_"
|
38
|
+
}
|
39
|
+
Write-Error-Message $errortHash
|
40
|
+
return
|
41
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#-------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
|
6
|
+
param (
|
7
|
+
[string]$vm_id = $(throw "-vm_id is required."),
|
8
|
+
[string]$path = $(throw "-path is required.")
|
9
|
+
)
|
10
|
+
|
11
|
+
# Include the following modules
|
12
|
+
$presentDir = Split-Path -parent $PSCommandPath
|
13
|
+
. ([System.IO.Path]::Combine($presentDir, "utils\write_messages.ps1"))
|
14
|
+
|
15
|
+
|
16
|
+
# Export the Virtual Machine
|
17
|
+
try {
|
18
|
+
$vm = Get-Vm -Id $vm_id
|
19
|
+
$vm | Export-VM -Path $path -ErrorAction "stop"
|
20
|
+
$name = $vm.name
|
21
|
+
$resultHash = @{
|
22
|
+
name = "$name"
|
23
|
+
}
|
24
|
+
Write-Output-Message $resultHash
|
25
|
+
} catch {
|
26
|
+
$errortHash = @{
|
27
|
+
type = "PowerShellError"
|
28
|
+
error = "Failed to export a VM $_"
|
29
|
+
}
|
30
|
+
Write-Error-Message $errortHash
|
31
|
+
}
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#-------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache 2.0 License.
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
|
6
|
+
param (
|
7
|
+
[string]$vm_id = $(throw "-vm_id is required."),
|
8
|
+
[string]$guest_ip = $(throw "-guest_ip is required."),
|
9
|
+
[string]$username = $(throw "-guest_username is required."),
|
10
|
+
[string]$password = $(throw "-guest_password is required."),
|
11
|
+
[string]$host_path = $(throw "-host_path is required."),
|
12
|
+
[string]$guest_path = $(throw "-guest_path is required.")
|
13
|
+
)
|
14
|
+
|
15
|
+
# Include the following modules
|
16
|
+
$presentDir = Split-Path -parent $PSCommandPath
|
17
|
+
$modules = @()
|
18
|
+
$modules += $presentDir + "\utils\write_messages.ps1"
|
19
|
+
$modules += $presentDir + "\utils\create_session.ps1"
|
20
|
+
forEach ($module in $modules) { . $module }
|
21
|
+
|
22
|
+
function Get-file-hash($source_path, $delimiter) {
|
23
|
+
$source_files = @()
|
24
|
+
(Get-ChildItem $source_path -rec -force | ForEach-Object -Process {
|
25
|
+
Get-FileHash -Path $_.FullName -Algorithm MD5 } ) |
|
26
|
+
ForEach-Object -Process {
|
27
|
+
$source_files += $_.Path.Replace($source_path, "") + $delimiter + $_.Hash
|
28
|
+
}
|
29
|
+
$source_files
|
30
|
+
}
|
31
|
+
|
32
|
+
function Get-remote-file-hash($source_path, $delimiter, $session) {
|
33
|
+
Invoke-Command -Session $session -ScriptBlock ${function:Get-file-hash} -ArgumentList $source_path, $delimiter
|
34
|
+
# TODO:
|
35
|
+
# Check if remote PS Scripting errors
|
36
|
+
}
|
37
|
+
|
38
|
+
function Sync-Remote-Machine($machine, $remove_files, $copy_files, $host_path, $guest_path) {
|
39
|
+
ForEach ($item in $copy_files) {
|
40
|
+
$from = $host_path + $item
|
41
|
+
$to = $guest_path + $item
|
42
|
+
# Copy VM can also take a VM object
|
43
|
+
Copy-VMFile -VM $machine -SourcePath $from -DestinationPath $to -CreateFullPath -FileSource Host -Force
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
function Create-Remote-Folders($empty_source_folders, $guest_path) {
|
48
|
+
|
49
|
+
ForEach ($item in $empty_source_folders) {
|
50
|
+
$new_name = $guest_path + $item
|
51
|
+
New-Item "$new_name" -type directory -Force
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
function Create-Guest-Folder($guest_path) {
|
56
|
+
try {
|
57
|
+
if (Test-Path $guest_path) {
|
58
|
+
$junction = Get-Item $guest_path
|
59
|
+
$junction.Delete()
|
60
|
+
}
|
61
|
+
}
|
62
|
+
# Catch any [IOException]
|
63
|
+
catch {
|
64
|
+
Remove-Item "$guest_path" -Force -Recurse
|
65
|
+
}
|
66
|
+
New-Item "$guest_path" -type directory -Force
|
67
|
+
}
|
68
|
+
|
69
|
+
function Get-Empty-folders-From-Source($host_path) {
|
70
|
+
Get-ChildItem $host_path -recurse |
|
71
|
+
Where-Object {$_.PSIsContainer -eq $True} |
|
72
|
+
Where-Object {$_.GetFiles().Count -eq 0} |
|
73
|
+
Select-Object FullName | ForEach-Object -Process {
|
74
|
+
$empty_source_folders += ($_.FullName.Replace($host_path, ""))
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
$delimiter = " || "
|
79
|
+
|
80
|
+
$machine = Get-VM -Id $vm_id
|
81
|
+
|
82
|
+
# FIXME: PowerShell guys please fix this.
|
83
|
+
# The below script checks for all VMIntegrationService which are not enabled
|
84
|
+
# and will enable this.
|
85
|
+
# When when all the services are enabled this throws an error.
|
86
|
+
# Enable VMIntegrationService to true
|
87
|
+
try {
|
88
|
+
Get-VM -Id $vm_id | Get-VMIntegrationService -Name "Guest Service Interface" | Enable-VMIntegrationService -Passthru
|
89
|
+
}
|
90
|
+
catch { }
|
91
|
+
|
92
|
+
|
93
|
+
$response = Create-Remote-Session $guest_ip $username $password
|
94
|
+
if (!$response["session"] -and $response["error"]) {
|
95
|
+
$errortHash = @{
|
96
|
+
type = "PowerShellError"
|
97
|
+
error = $response["error"]
|
98
|
+
}
|
99
|
+
Write-Error-Message $errortHash
|
100
|
+
return
|
101
|
+
}
|
102
|
+
|
103
|
+
$session = $response["session"]
|
104
|
+
# Create the guest folder if not exist
|
105
|
+
$result = Invoke-Command -Session $session -ScriptBlock ${function:Create-Guest-Folder} -ArgumentList $guest_path
|
106
|
+
|
107
|
+
|
108
|
+
$source_files = Get-file-hash $host_path $delimiter
|
109
|
+
$destination_files = Get-remote-file-hash $guest_path $delimiter $session
|
110
|
+
|
111
|
+
if (!$destination_files) {
|
112
|
+
$destination_files = @()
|
113
|
+
}
|
114
|
+
if (!$source_files) {
|
115
|
+
$source_files = @()
|
116
|
+
}
|
117
|
+
|
118
|
+
# Compare source and destination files
|
119
|
+
$remove_files = @()
|
120
|
+
$copy_files = @()
|
121
|
+
|
122
|
+
|
123
|
+
Compare-Object -ReferenceObject $source_files -DifferenceObject $destination_files | ForEach-Object {
|
124
|
+
if ($_.SideIndicator -eq '=>') {
|
125
|
+
$remove_files += $_.InputObject.Split($delimiter)[0]
|
126
|
+
} else {
|
127
|
+
$copy_files += $_.InputObject.Split($delimiter)[0]
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
# Update the files to remote machine
|
132
|
+
Sync-Remote-Machine $machine $remove_files $copy_files $host_path $guest_path
|
133
|
+
|
134
|
+
# Create any empty folders which missed to sync to remote machine
|
135
|
+
$empty_source_folders = @()
|
136
|
+
$directories = Get-Empty-folders-From-Source $host_path
|
137
|
+
|
138
|
+
$result = Invoke-Command -Session $session -ScriptBlock ${function:Create-Remote-Folders} -ArgumentList $empty_source_folders, $guest_path
|
139
|
+
# Always remove the connection after Use
|
140
|
+
Remove-PSSession -Id $session.Id
|
141
|
+
|
142
|
+
$resultHash = @{
|
143
|
+
message = "OK"
|
144
|
+
}
|
145
|
+
Write-Output-Message $resultHash
|