diff --git a/lib/ansible/modules/windows/win_defrag.ps1 b/lib/ansible/modules/windows/win_defrag.ps1 new file mode 100644 index 00000000000..666ffee6ed1 --- /dev/null +++ b/lib/ansible/modules/windows/win_defrag.ps1 @@ -0,0 +1,163 @@ +#!powershell +# This file is part of Ansible +# +# (c) 2017, Dag Wieers +# +# 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 + +$ErrorActionPreference = "Stop" + +$params = Parse-Args $args -supports_check_mode $true +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false + +$include_volumes = Get-AnsibleParam -obj $params -name "include_volumes" -type "list" +$exclude_volumes = Get-AnsibleParam -obj $params -name "exclude_volumes" -type "list" +$freespace_consolidation = Get-AnsibleParam -obj $params -name "freespace_consolidation" -type "bool" -default $false +$priority = Get-AnsibleParam -obj $params -name "priority" -type "string" -default "low" -validateset "low","normal" +$parallel = Get-AnsibleParam -obj $params -name "parallel" -type "bool" -default $false + +$result = @{ + changed = $false +} + +$executable = "defrag.exe" + +if (-not (Get-Command -Name $executable -ErrorAction SilentlyContinue)) { + Fail-Json $result "Command '$executable' not found in $env:PATH." +} + +$util_def = @' +using System; +using System.ComponentModel; +using System.IO; +using System.Threading; + +namespace Ansible.Command { + + public static class NativeUtil { + + public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr) { + var sowait = new EventWaitHandle(false, EventResetMode.ManualReset); + var sewait = new EventWaitHandle(false, EventResetMode.ManualReset); + + string so = null, se = null; + + ThreadPool.QueueUserWorkItem((s)=> { + so = stdoutStream.ReadToEnd(); + sowait.Set(); + }); + + ThreadPool.QueueUserWorkItem((s) => { + se = stderrStream.ReadToEnd(); + sewait.Set(); + }); + + foreach(var wh in new WaitHandle[] { sowait, sewait }) + wh.WaitOne(); + + stdout = so; + stderr = se; + } + } +} +'@ + +$util_type = Add-Type -TypeDefinition $util_def + +$arguments = "" + +if ($include_volumes) { + foreach ($volume in $include_volumes) { + if ($volume.Length == 1) { + $arguments += " $($volume):" + } else { + $arguments += " $volume" + } + } +} else { + $arguments = " /C" +} + +if ($exclude_volumes) { + $arguments += " /E" + foreach ($volume in $exclude_volumes) { + if ($volume.Length == 1) { + $arguments += " $($volume):" + } else { + $arguments += " $volume" + } + } +} + +if ($check_mode) { + $arguments += " /A" +} elseif ($freespace_consolidation) { + $arguments += " /X" +} + +if ($priority -eq "normal") { + $arguments += " /H" +} + +if ($parallel) { + $arguments += " /M" +} + +$arguments += " /V" + +$proc = New-Object System.Diagnostics.Process +$psi = $proc.StartInfo +$psi.FileName = $executable +$psi.Arguments = $arguments +$psi.RedirectStandardOutput = $true +$psi.RedirectStandardError = $true +$psi.UseShellExecute = $false + +$result.cmd = "$executable$arguments" + +$start_datetime = [DateTime]::UtcNow + +Try { + $proc.Start() | Out-Null # will always return $true for non shell-exec cases +} Catch [System.ComponentModel.Win32Exception] { + # fail nicely for "normal" error conditions + # FUTURE: this probably won't work on Nano Server + $excep = $_ + $result.rc = $excep.Exception.NativeErrorCode + Fail-Json $result $excep.Exception.Message +} + +$stdout = $stderr = [string] $null + +[Ansible.Command.NativeUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null + +$result.stdout = $stdout +$result.stderr = $stderr + +$proc.WaitForExit() | Out-Null + +$result.rc = $proc.ExitCode + +$end_datetime = [DateTime]::UtcNow + +$result.start = $start_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") +$result.end = $end_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") +$result.delta = $($end_datetime - $start_datetime).ToString("h\:mm\:ss\.ffffff") + +$result.changed = $true + +Exit-Json $result diff --git a/lib/ansible/modules/windows/win_defrag.py b/lib/ansible/modules/windows/win_defrag.py new file mode 100644 index 00000000000..e774ae7e183 --- /dev/null +++ b/lib/ansible/modules/windows/win_defrag.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright 2017, Dag Wieers +# +# 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 . + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = r''' +--- +module: win_defrag +version_added: '2.4' +short_description: Consolidate fragmented files on local volumes. +description: +- Locates and consolidates fragmented files on local volumes to improve system performance. +- 'More information regarding C(win_defrag) is available from: U(https://technet.microsoft.com/en-us/library/cc731650(v=ws.11).aspx)' +options: + included_volumes: + description: + - A list of drive letters or mount point paths of the volumes to be defragmented. + - If this parameter is omitted, all volumes (not excluded) will be fragmented. + excluded_volumes: + description: + - A list of drive letters or mount point paths to exclude from defragmentation. + freespace_consolidation: + description: + - Perform free space consolidation on the specified volumes. + priority: + description: + - Run the operation at low or normal priority. + default: low + choices: [ low, normal ] + parallel: + description: + - Run the operation on each volume in parallel in the background. + default: 'no' + choices: [ 'yes', 'no' ] +requirements: [ defrag.exe ] +author: Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Defragment all local volumes (in parallel) + win_defrag: + parallel: yes + +- name: 'Defragment all local volumes, except C: and D:' + win_defrag: + exclude_volumes: [ C, D ] + +- name: 'Defragment volume D: with normal priority' + win_defrag: + include_volumes: D + priority: normal + +- name: Consolidate free space (useful when reducing volumes) + win_defrag: + freespace_consolidation: yes +''' + +RETURN = r''' +cmd: + description: The complete command line used by the module + returned: always + type: string + sample: defrag.exe /C /V +rc: + description: The return code for the command + returned: always + type: int + sample: 0 +stdout: + description: The standard output from the command + returned: always + type: string + sample: Success. +stderr: + description: The error output from the command + returned: always + type: string + sample: +msg: + description: Possible error message on failure + returned: failed + type: string + sample: Command 'defrag.exe' not found in $env:PATH. +changed: + description: Whether or not any changes were made. + returned: always + type: bool + sample: True +'''