New Windows Module: win_file_compression (#56475)
* add win_compact module * fixed line endings * fix documentation * Use cim method instead of wmi method * renamed to win_file_compression added single file support added force option to avoid traversing large directory structures * fixed end of file * fixed renaming. bench test still had win_compact as a module * Removed more NTFS references and slight test tweaks
This commit is contained in:
parent
7084dd727a
commit
19063166dd
6 changed files with 450 additions and 0 deletions
lib/ansible/modules/windows
test/integration/targets/win_file_compression
118
lib/ansible/modules/windows/win_file_compression.ps1
Normal file
118
lib/ansible/modules/windows/win_file_compression.ps1
Normal file
|
@ -0,0 +1,118 @@
|
|||
#!powershell
|
||||
|
||||
# Copyright: (c) 2019, Micah Hunsberger
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
|
||||
Set-StrictMode -Version 2
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
path = @{ type = 'path'; required = $true }
|
||||
state = @{ type = 'str'; default = 'present'; choices = 'absent', 'present' }
|
||||
recurse = @{ type = 'bool'; default = $false }
|
||||
force = @{ type = 'bool'; default = $true }
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$path = $module.Params.path
|
||||
$state = $module.Params.state
|
||||
$recurse = $module.Params.recurse
|
||||
$force = $module.Params.force
|
||||
|
||||
$module.Result.rc = 0
|
||||
|
||||
if(-not (Test-Path -LiteralPath $path)) {
|
||||
$module.FailJson("Path to item, $path, does not exist.")
|
||||
}
|
||||
|
||||
$item = Get-Item -LiteralPath $path -Force # Use -Force for hidden files
|
||||
if (-not $item.PSIsContainer -and $recurse) {
|
||||
$module.Warn("The recurse option has no effect when path is not a folder.")
|
||||
}
|
||||
|
||||
$cim_params = @{
|
||||
ClassName = 'Win32_LogicalDisk'
|
||||
Filter = "DeviceId='$($item.PSDrive.Name):'"
|
||||
Property = @('FileSystem', 'SupportsFileBasedCompression')
|
||||
}
|
||||
$drive_info = Get-CimInstance @cim_params
|
||||
if ($drive_info.SupportsFileBasedCompression -eq $false) {
|
||||
$module.FailJson("Path, $path, is not on a filesystemi '$($drive_info.FileSystem)' that supports file based compression.")
|
||||
}
|
||||
|
||||
function Get-ReturnCodeMessage {
|
||||
param(
|
||||
[int]$code
|
||||
)
|
||||
switch ($code) {
|
||||
0 { return "The request was successful." }
|
||||
2 { return "Access was denied." }
|
||||
8 { return "An unspecified failure occurred." }
|
||||
9 { return "The name specified was not valid." }
|
||||
10 { return "The object specified already exists." }
|
||||
11 { return "The file system is not NTFS." }
|
||||
12 { return "The platform is not Windows." }
|
||||
13 { return "The drive is not the same." }
|
||||
14 { return "The directory is not empty." }
|
||||
15 { return "There has been a sharing violation." }
|
||||
16 { return "The start file specified was not valid." }
|
||||
17 { return "A privilege required for the operation is not held." }
|
||||
21 { return "A parameter specified is not valid." }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-EscapedFileName {
|
||||
param(
|
||||
[string]$FullName
|
||||
)
|
||||
return $FullName.Replace("\","\\").Replace("'","\'")
|
||||
}
|
||||
|
||||
$is_compressed = ($item.Attributes -band [System.IO.FileAttributes]::Compressed) -eq [System.IO.FileAttributes]::Compressed
|
||||
$needs_changed = $is_compressed -ne ($state -eq 'present')
|
||||
|
||||
if($force -and $recurse -and $item.PSIsContainer) {
|
||||
if (-not $needs_changed) {
|
||||
# Check the subfolders and files
|
||||
$entries_to_check = $item.EnumerateFileSystemInfos("*", [System.IO.SearchOption]::AllDirectories)
|
||||
foreach ($entry in $entries_to_check) {
|
||||
$is_compressed = ($entry.Attributes -band [System.IO.FileAttributes]::Compressed) -eq [System.IO.FileAttributes]::Compressed
|
||||
if ($is_compressed -ne ($state -eq 'present')) {
|
||||
$needs_changed = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($needs_changed) {
|
||||
$module.Result.changed = $true
|
||||
if ($item.PSIsContainer) {
|
||||
$cim_obj = Get-CimInstance -ClassName 'Win32_Directory' -Filter "Name='$(Get-EscapedFileName -FullName $item.FullName)'"
|
||||
} else {
|
||||
$cim_obj = Get-CimInstance -ClassName 'CIM_LogicalFile' -Filter "Name='$(Get-EscapedFileName -FullName $item.FullName)'"
|
||||
}
|
||||
if($state -eq 'present') {
|
||||
if(-not $module.CheckMode) {
|
||||
$ret = Invoke-CimMethod -InputObject $cim_obj -MethodName 'CompressEx' -Arguments @{ Recursive = $recurse }
|
||||
$module.Result.rc = $ret.ReturnValue
|
||||
}
|
||||
} else {
|
||||
if(-not $module.CheckMode) {
|
||||
$ret = $ret = Invoke-CimMethod -InputObject $cim_obj -MethodName 'UnCompressEx' -Arguments @{ Recursive = $recurse }
|
||||
$module.Result.rc = $ret.ReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$module.Result.msg = Get-ReturnCodeMessage -code $module.Result.rc
|
||||
if($module.Result.rc -ne 0) {
|
||||
$module.FailJson($module.Result.msg)
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
100
lib/ansible/modules/windows/win_file_compression.py
Normal file
100
lib/ansible/modules/windows/win_file_compression.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Micah Hunsberger (@mhunsber)
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# this is a windows documentation stub. actual code lives in the .ps1
|
||||
# file of the same name
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_file_compression
|
||||
version_added: '2.10'
|
||||
short_description: Alters the compression of files and directories on NTFS partitions.
|
||||
description:
|
||||
- This module sets the compressed attribute for files and directories on a filesystem that supports it like NTFS.
|
||||
- NTFS compression can be used to save disk space.
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- The full path of the file or directory to modify.
|
||||
- The path must exist on file system that supports compression like NTFS.
|
||||
required: yes
|
||||
type: path
|
||||
state:
|
||||
description:
|
||||
- Set to C(present) to ensure the I(path) is compressed.
|
||||
- Set to C(absent) to ensure the I(path) is not compressed.
|
||||
type: str
|
||||
choices:
|
||||
- absent
|
||||
- present
|
||||
default: present
|
||||
recurse:
|
||||
description:
|
||||
- Whether to recursively apply changes to all subdirectories and files.
|
||||
- This option only has an effect when I(path) is a directory.
|
||||
- When set to C(false), only applies changes to I(path).
|
||||
- When set to C(true), applies changes to I(path) and all subdirectories and files.
|
||||
type: bool
|
||||
default: false
|
||||
force:
|
||||
description:
|
||||
- This option only has an effect when I(recurse) is C(true)
|
||||
- If C(true), will check the compressed state of all subdirectories and files
|
||||
and make a change if any are different from I(compressed).
|
||||
- If C(false), will only make a change if the compressed state of I(path) is different from I(compressed).
|
||||
- If the folder structure is complex or contains a lot of files, it is recommended to set this
|
||||
option to C(false) so that not every file has to be checked.
|
||||
type: bool
|
||||
default: true
|
||||
author:
|
||||
- Micah Hunsberger (@mhunsber)
|
||||
notes:
|
||||
- C(win_file_compression) sets the file system's compression state, it does not create a zip archive file.
|
||||
- For more about NTFS Compression, see U(http://www.ntfs.com/ntfs-compressed.htm)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Compress log files directory
|
||||
win_file_compression:
|
||||
path: C:\Logs
|
||||
state: present
|
||||
|
||||
- name: Decompress log files directory
|
||||
win_file_compression:
|
||||
path: C:\Logs
|
||||
state: absent
|
||||
|
||||
- name: Compress reports directory and all subdirectories
|
||||
win_file_compression:
|
||||
path: C:\business\reports
|
||||
state: present
|
||||
recurse: yes
|
||||
|
||||
# This will only check C:\business\reports for the compressed state
|
||||
# If C:\business\reports is compressed, it will not make a change
|
||||
# even if one of the child items is uncompressed
|
||||
|
||||
- name: Compress reports directory and all subdirectories (quick)
|
||||
win_file_compression:
|
||||
path: C:\business\reports
|
||||
compressed: yes
|
||||
recurse: yes
|
||||
force: no
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
rc:
|
||||
description:
|
||||
- The return code of the compress/uncompress operation.
|
||||
- If no changes are made or the operation is successful, rc is 0.
|
||||
returned: always
|
||||
sample: 0
|
||||
type: int
|
||||
'''
|
1
test/integration/targets/win_file_compression/aliases
Normal file
1
test/integration/targets/win_file_compression/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
shippable/windows/group6
|
|
@ -0,0 +1,5 @@
|
|||
test_win_file_compression_suffix: win_file_compression .ÅÑŚÌβŁÈ [$!@^&test(;)]
|
||||
test_win_file_compression_sub_directories:
|
||||
- 'a'
|
||||
- 'b'
|
||||
test_win_file_compression_filename: 'foo.bar'
|
|
@ -0,0 +1,2 @@
|
|||
dependencies:
|
||||
- setup_remote_tmp_dir
|
224
test/integration/targets/win_file_compression/tasks/main.yml
Normal file
224
test/integration/targets/win_file_compression/tasks/main.yml
Normal file
|
@ -0,0 +1,224 @@
|
|||
---
|
||||
- name: set fact of special testing dir
|
||||
set_fact:
|
||||
test_directory: '{{ remote_tmp_dir }}\{{ test_win_file_compression_suffix }}'
|
||||
|
||||
- name: create sub directories
|
||||
win_file:
|
||||
state: directory
|
||||
path: "{{ test_directory }}\\{{ item }}"
|
||||
loop: "{{ test_win_file_compression_sub_directories }}"
|
||||
|
||||
- name: set main directory as hidden to test out edge cases
|
||||
win_shell: (Get-Item -LiteralPath '{{ test_directory }}').Attributes = [System.IO.FileAttributes]::Hidden
|
||||
|
||||
- name: Compress parent directory
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes for parent directory
|
||||
win_stat:
|
||||
path: "{{ test_directory }}"
|
||||
register: folder_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' in folder_info.stat.attributes"
|
||||
- "result.changed == true"
|
||||
|
||||
- name: Get actual attributes for sub directories
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ item }}"
|
||||
register: subfolder_info
|
||||
loop: "{{ test_win_file_compression_sub_directories }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' not in item.stat.attributes"
|
||||
loop: "{{ subfolder_info.results }}"
|
||||
|
||||
- name: Compress parent directory (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Compress parent directory and all subdirectories
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: present
|
||||
recurse: yes
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes for parent directory
|
||||
win_stat:
|
||||
path: "{{ test_directory }}"
|
||||
register: folder_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' in folder_info.stat.attributes"
|
||||
- "result.changed == true"
|
||||
|
||||
- name: Get actual attributes for sub directories
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ item }}"
|
||||
register: subfolder_info
|
||||
loop: "{{ test_win_file_compression_sub_directories }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' in item.stat.attributes"
|
||||
loop: "{{ subfolder_info.results }}"
|
||||
|
||||
- name: Compress parent directory and all subdirectories (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: present
|
||||
recurse: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Uncompress parent directory
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: absent
|
||||
recurse: no
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes for parent directory
|
||||
win_stat:
|
||||
path: "{{ test_directory }}"
|
||||
register: folder_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' not in folder_info.stat.attributes"
|
||||
- "result.changed == true"
|
||||
|
||||
- name: Get actual attributes for sub directories
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ item }}"
|
||||
register: subfolder_info
|
||||
loop: "{{ test_win_file_compression_sub_directories }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' in item.stat.attributes"
|
||||
loop: "{{ subfolder_info.results }}"
|
||||
|
||||
- name: Uncompress parent directory (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: absent
|
||||
recurse: no
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Uncompress parent directory and all subdirectories
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: absent
|
||||
recurse: yes
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes for parent directory
|
||||
win_stat:
|
||||
path: "{{ test_directory }}"
|
||||
register: folder_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' not in folder_info.stat.attributes"
|
||||
- "result.changed == true"
|
||||
|
||||
- name: Get actual attributes for sub directories
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ item }}"
|
||||
register: subfolder_info
|
||||
loop: "{{ test_win_file_compression_sub_directories }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'Compressed' not in item.stat.attributes"
|
||||
loop: "{{ subfolder_info.results }}"
|
||||
|
||||
- name: Uncompress parent directory and all subdirectories (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}"
|
||||
state: absent
|
||||
recurse: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Create test file
|
||||
win_file:
|
||||
state: touch
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
|
||||
- name: Compress specific file
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes of file
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
register: testfile_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'Compressed' in testfile_info.stat.attributes"
|
||||
|
||||
- name: Compress specific file (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Uncompress specific file
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- name: Get actual attributes of file
|
||||
win_stat:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
register: testfile_info
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'Compressed' not in testfile_info.stat.attributes"
|
||||
|
||||
- name: Uncompress specific file (idempotent)
|
||||
win_file_compression:
|
||||
path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}"
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
Loading…
Add table
Reference in a new issue