diff --git a/lib/ansible/modules/extras/windows/win_robocopy.ps1 b/lib/ansible/modules/extras/windows/win_robocopy.ps1 new file mode 100644 index 0000000000..69cf9ee3e3 --- /dev/null +++ b/lib/ansible/modules/extras/windows/win_robocopy.ps1 @@ -0,0 +1,147 @@ +#!powershell +# This file is part of Ansible +# +# Copyright 2015, Corwin Brown +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# WANT_JSON +# POWERSHELL_COMMON + +$params = Parse-Args $args; + +$result = New-Object psobject @{ + win_robocopy = New-Object psobject @{ + recurse = $false + purge = $false + } + changed = $false +} + +$src = Get-AnsibleParam -obj $params -name "src" -failifempty $true +$dest = Get-AnsibleParam -obj $params -name "dest" -failifempty $true +$purge = ConvertTo-Bool (Get-AnsibleParam -obj $params -name "purge" -default $false) +$recurse = ConvertTo-Bool (Get-AnsibleParam -obj $params -name "recurse" -default $false) +$flags = Get-AnsibleParam -obj $params -name "flags" -default $null +$_ansible_check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false + +# Search for an Error Message +# Robocopy seems to display an error after 3 '-----' separator lines +Function SearchForError($cmd_output, $default_msg) { + $separator_count = 0 + $error_msg = $default_msg + ForEach ($line in $cmd_output) { + if (-Not $line) { + continue + } + + if ($separator_count -ne 3) { + if (Select-String -InputObject $line -pattern "^(\s+)?(\-+)(\s+)?$") { + $separator_count += 1 + } + } + Else { + If (Select-String -InputObject $line -pattern "error") { + $error_msg = $line + break + } + } + } + + return $error_msg +} + +# Build Arguments +$robocopy_opts = @() + +if (-Not (Test-Path $src)) { + Fail-Json $result "$src does not exist!" +} + +$robocopy_opts += $src +Set-Attr $result.win_robocopy "src" $src + +$robocopy_opts += $dest +Set-Attr $result.win_robocopy "dest" $dest + +if ($flags -eq $null) { + if ($purge) { + $robocopy_opts += "/purge" + } + + if ($recurse) { + $robocopy_opts += "/e" + } +} +Else { + $robocopy_opts += $flags +} + +Set-Attr $result.win_robocopy "purge" $purge +Set-Attr $result.win_robocopy "recurse" $recurse +Set-Attr $result.win_robocopy "flags" $flags + +$robocopy_output = "" +$rc = 0 +If ($_ansible_check_mode -eq $true) { + $robocopy_output = "Would have copied the contents of $src to $dest" + $rc = 0 +} +Else { + Try { + &robocopy $robocopy_opts | Tee-Object -Variable robocopy_output | Out-Null + $rc = $LASTEXITCODE + } + Catch { + $ErrorMessage = $_.Exception.Message + Fail-Json $result "Error synchronizing $src to $dest! Msg: $ErrorMessage" + } +} + +Set-Attr $result.win_robocopy "return_code" $rc +Set-Attr $result.win_robocopy "output" $robocopy_output + +$cmd_msg = "Success" +If ($rc -eq 0) { + $cmd_msg = "No files copied." +} +ElseIf ($rc -eq 1) { + $cmd_msg = "Files copied successfully!" + $changed = $true +} +ElseIf ($rc -eq 2) { + $cmd_msg = "Extra files or directories were detected!" + $changed = $true +} +ElseIf ($rc -eq 4) { + $cmd_msg = "Some mismatched files or directories were detected!" + $changed = $true +} +ElseIf ($rc -eq 8) { + $error_msg = SearchForError $robocopy_output "Some files or directories could not be copied!" + Fail-Json $result $error_msg +} +ElseIf ($rc -eq 10) { + $error_msg = SearchForError $robocopy_output "Serious Error! No files were copied! Do you have permissions to access $src and $dest?" + Fail-Json $result $error_msg +} +ElseIf ($rc -eq 16) { + $error_msg = SearchForError $robocopy_output "Fatal Error!" + Fail-Json $result $error_msg +} + +Set-Attr $result.win_robocopy "msg" $cmd_msg +Set-Attr $result.win_robocopy "changed" $changed + +Exit-Json $result diff --git a/lib/ansible/modules/extras/windows/win_robocopy.py b/lib/ansible/modules/extras/windows/win_robocopy.py new file mode 100644 index 0000000000..d627918e52 --- /dev/null +++ b/lib/ansible/modules/extras/windows/win_robocopy.py @@ -0,0 +1,143 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2015, Corwin Brown +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# this is a windows documentation stub. actual code lives in the .ps1 +# file of the same name + +DOCUMENTATION = """ +--- +module: win_robocopy +version_added: "2.2" +short_description: Synchronizes the contents of two directories using Robocopy. +description: + - Synchronizes the contents of two directories on the remote machine. Under the hood this just calls out to RoboCopy, since that should be available on most modern Windows Systems. +options: + src: + description: + - Source file/directory to sync. + required: true + dest: + description: + - Destination file/directory to sync (Will receive contents of src). + required: true + recurse: + description: + - Includes all subdirectories (Toggles the `/e` flag to RoboCopy). If "flags" is set, this will be ignored. + choices: + - true + - false + defaults: false + required: false + purge: + description: + - Deletes any files/directories found in the destination that do not exist in the source (Toggles the `/purge` flag to RoboCopy). If "flags" is set, this will be ignored. + choices: + - true + - false + defaults: false + required: false + flags: + description: + - Directly supply Robocopy flags. If set, purge and recurse will be ignored. + default: None + required: false +author: Corwin Brown (@blakfeld) +notes: + - This is not a complete port of the "synchronize" module. Unlike the "synchronize" module this only performs the sync/copy on the remote machine, not from the master to the remote machine. + - This module does not currently support all Robocopy flags. + - Works on Windows 7, Windows 8, Windows Server 2k8, and Windows Server 2k12 +""" + +EXAMPLES = """ +# Syncs the contents of one diretory to another. +$ ansible -i hosts all -m win_robocopy -a "src=C:\\DirectoryOne dest=C:\\DirectoryTwo" + +# Sync the contents of one directory to another, including subdirectories. +$ ansible -i hosts all -m win_robocopy -a "src=C:\\DirectoryOne dest=C:\\DirectoryTwo recurse=true" + +# Sync the contents of one directory to another, and remove any files/directories found in destination that do not exist in the source. +$ ansible -i hosts all -m win_robocopy -a "src=C:\\DirectoryOne dest=C:\\DirectoryTwo purge=true" + +# Sample sync +--- +- name: Sync Two Directories + win_robocopy: + src: "C:\\DirectoryOne + dest: "C:\\DirectoryTwo" + recurse: true + purge: true + +--- +- name: Sync Two Directories + win_robocopy: + src: "C:\\DirectoryOne + dest: "C:\\DirectoryTwo" + recurse: true + purge: true + flags: '/XD SOME_DIR /XF SOME_FILE /MT:32' +""" + +RETURN = ''' +src: + description: The Source file/directory of the sync. + returned: always + type: string + sample: "c:/Some/Path" +dest: + description: The Destination file/directory of the sync. + returned: always + type: string + sample: "c:/Some/Path" +recurse: + description: Whether or not the recurse flag was toggled. + returned: always + type: bool + sample: False +purge: + description: Whether or not the purge flag was toggled. + returned: always + type: bool + sample: False +flags: + description: Any flags passed in by the user. + returned: always + type: string + sample: "/e /purge" +return_code: + description: The return code retuned by robocopy. + returned: success + type: int + sample: 1 +output: + description: The output of running the robocopy command. + returned: success + type: string + sample: "-------------------------------------------------------------------------------\n ROBOCOPY :: Robust File Copy for Windows \n-------------------------------------------------------------------------------\n" +msg: + description: Output intrepreted into a concise message. + returned: always + type: string + sample: No files copied! +changed: + description: Whether or not any changes were made. + returned: always + type: bool + sample: False +'''