Added win_find module (#19144)

This commit is contained in:
Jordan Borean 2017-02-09 08:19:08 +10:00 committed by John R Barker
parent 57a5490c41
commit 1ce5fcf061
11 changed files with 1452 additions and 0 deletions

View file

@ -233,6 +233,7 @@ Ansible Changes By Release
- web_infrastructure
* jenkins_script
- windows:
* win_find
* win_path
* win_psexec
* win_say

View file

@ -0,0 +1,347 @@
#!powershell
# 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 <http://www.gnu.org/licenses/>.
# WANT_JSON
# POWERSHELL_COMMON
$ErrorActionPreference = "Stop"
$params = Parse-Args -arguments $args -supports_check_mode $true
$paths = Get-AnsibleParam -obj $params -name 'paths' -failifempty $true
$age = Get-AnsibleParam -obj $params -name 'age' -failifempty $false -default $null
$age_stamp = Get-AnsibleParam -obj $params -name 'age_stamp' -failifempty $false -default 'mtime' -ValidateSet 'mtime','ctime','atime'
$file_type = Get-AnsibleParam -obj $params -name 'file_type' -failifempty $false -default 'file' -ValidateSet 'file','directory'
$follow = Get-AnsibleParam -obj $params -name 'follow' -type "bool" -failifempty $false -default $false
$hidden = Get-AnsibleParam -obj $params -name 'hidden' -type "bool" -failifempty $false -default $false
$patterns = Get-AnsibleParam -obj $params -name 'patterns' -failifempty $false -default $null
$recurse = Get-AnsibleParam -obj $params -name 'recurse' -type "bool" -failifempty $false -default $false
$size = Get-AnsibleParam -obj $params -name 'size' -failifempty $false -default $null
$use_regex = Get-AnsibleParam -obj $params -name 'use_regex' -type "bool" -failifempty $false -default $false
$get_checksum = Get-AnsibleParam -obj $params -name 'get_checksum' -type "bool" -failifempty $false -default $true
$checksum_algorithm = Get-AnsibleParam -obj $params -name 'checksum_algorithm' -failifempty $false -default 'sha1' -ValidateSet 'md5', 'sha1', 'sha256', 'sha384', 'sha512'
$result = @{
files = @()
warnings = @()
examined = 0
matched = 0
changed = $false
}
# C# code to determine link target, copied from http://chrisbensen.blogspot.com.au/2010/06/getfinalpathnamebyhandle.html
$symlink_util = @"
using System;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Ansible.Command {
public class SymLinkHelper {
private const int FILE_SHARE_WRITE = 2;
private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess,
int dwShareMode, IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink) {
SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
if(directoryHandle.IsInvalid)
throw new Win32Exception(Marshal.GetLastWin32Error());
StringBuilder path = new StringBuilder(512);
int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
if (size<0)
throw new Win32Exception(Marshal.GetLastWin32Error()); // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
return path.ToString().Substring(4);
else
return path.ToString();
}
}
}
"@
Add-Type -TypeDefinition $symlink_util
Function Assert-Age($info) {
$valid_match = $true
if ($age -ne $null) {
$seconds_per_unit = @{'s'=1; 'm'=60; 'h'=3600; 'd'=86400; 'w'=604800}
$seconds_pattern = '^(-?\d+)(s|m|h|d|w)?$'
$match = $age -match $seconds_pattern
if ($match) {
[int]$specified_seconds = $matches[1]
if ($matches[2] -eq $null) {
$chosen_unit = 's'
} else {
$chosen_unit = $matches[2]
}
$abs_seconds = $specified_seconds * ($seconds_per_unit.$chosen_unit)
$epoch = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
if ($age_stamp -eq 'mtime') {
$age_comparison = $epoch.AddSeconds($info.lastwritetime)
} elseif ($age_stamp -eq 'ctime') {
$age_comparison = $epoch.AddSeconds($info.creationtime)
} elseif ($age_stamp -eq 'atime') {
$age_comparison = $epoch.AddSeconds($info.lastaccesstime)
}
if ($specified_seconds -ge 0) {
$start_date = (Get-Date).AddSeconds($abs_seconds * -1)
if ($age_comparison -lt $start_date) {
$valid_match = $false
}
} else {
$start_date = (Get-Date).AddSeconds($abs_seconds)
if ($age_comparison -gt $start_date) {
$valid_match = $false
}
}
} else {
Fail-Json $result "failed to process age"
}
}
$valid_match
}
Function Assert-FileType($info) {
$valid_match = $true
if ($file_type -eq 'directory' -and $info.isdir -eq $false) {
$valid_match = $false
}
if ($file_type -eq 'file' -and $info.isdir -eq $true) {
$valid_match = $false
}
$valid_match
}
Function Assert-Hidden($info) {
$valid_match = $true
if ($hidden -eq $true -and $info.ishidden -eq $false) {
$valid_match = $false
}
if ($hidden -eq $false -and $info.ishidden -eq $true) {
$valid_match = $false
}
$valid_match
}
Function Assert-Pattern($info) {
$valid_match = $false
if ($patterns -ne $null) {
foreach ($pattern in $patterns) {
if ($use_regex -eq $true) {
# Use -match for regex matching
if ($info.filename -match $pattern) {
$valid_match = $true
}
} else {
# Use -like for wildcard matching
if ($info.filename -like $pattern) {
$valid_match = $true
}
}
}
} else {
$valid_match = $true
}
$valid_match
}
Function Assert-Size($info) {
$valid_match = $true
if ($size -ne $null) {
$bytes_per_unit = @{'b'=1; 'k'=1024; 'm'=1024*1024; 'g'=1024*1024*1024; 't'=1024*1024*1024*1024}
$size_pattern = '^(-?\d+)(b|k|m|g|t)?$'
$match = $size -match $size_pattern
if ($match) {
[int]$specified_size = $matches[1]
if ($matches[2] -eq $null) {
$chosen_byte = 'b'
} else {
$chosen_byte = $matches[2]
}
$abs_size = $specified_size * ($bytes_per_unit.$chosen_byte)
if ($specified_size -ge 0) {
if ($info.size -lt $abs_size) {
$valid_match = $false
}
} else {
if ($info.size -gt $abs_size * -1) {
$valid_match = $false
}
}
} else {
Fail-Json $result "failed to process size"
}
}
$valid_match
}
Function Assert-FileStat($info) {
$age_match = Assert-Age -info $info
$file_type_match = Assert-FileType -info $info
$hidden_match = Assert-Hidden -info $info
$pattern_match = Assert-Pattern -info $info
$size_match = Assert-Size -info $info
if ($age_match -and $file_type_match -and $hidden_match -and $pattern_match -and $size_match) {
$info
} else {
$false
}
}
Function Get-FileStat($file) {
$epoch = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
$access_control = $file.GetAccessControl()
$attributes = @()
foreach ($attribute in ($file.Attributes -split ',')) {
$attributes += $attribute.Trim()
}
$file_stat = @{
isreadonly = $attributes -contains 'ReadOnly'
ishidden = $attributes -contains 'Hidden'
isarchive = $attributes -contains 'Archive'
attributes = $file.Attributes.ToString()
owner = $access_control.Owner
lastwritetime = (New-TimeSpan -Start $epoch -End $file.LastWriteTime).TotalSeconds
creationtime = (New-TimeSpan -Start $epoch -End $file.CreationTime).TotalSeconds
lastaccesstime = (New-TimeSpan -Start $epoch -End $file.LastAccessTime).TotalSeconds
path = $file.FullName
filename = $file.Name
}
$islink = $false
$isdir = $false
$isshared = $false
if ($attributes -contains 'ReparsePoint') {
# TODO: Find a way to differenciate between soft and junction links
$islink = $true
$isdir = $true
# Try and get the symlink source, can result in failure if link is broken
try {
$lnk_source = [Ansible.Command.SymLinkHelper]::GetSymbolicLinkTarget($file)
$file_stat.lnk_source = $lnk_source
} catch {}
} elseif ($file.PSIsContainer) {
$isdir = $true
$share_info = Get-WmiObject -Class Win32_Share -Filter "Path='$($file.Fullname -replace '\\', '\\')'"
if ($share_info -ne $null) {
$isshared = $true
$file_stat.sharename = $share_info.Name
}
#$dir_files_sum = Get-ChildItem $file.FullName -Recurse | Measure-Object -property length -sum
$dir_files_sum = Get-ChildItem $file.FullName -Recurse
if ($dir_files_sum -eq $null -or ($dir_files_sum.PSObject.Properties.name -contains 'length' -eq $false)) {
$file_stat.size = 0
} else {
$file_stat.size = ($dir_files_sum | Measure-Object -property length -sum).Sum
}
} else {
$file_stat.size = $file.length
$file_stat.extension = $file.Extension
if ($get_checksum) {
$checksum = Get-FileChecksum -path $path -algorithm $checksum_algorithm
$file_stat.checksum = $checksum
}
}
$file_stat.islink = $islink
$file_stat.isdir = $isdir
$file_stat.isshared = $isshared
Assert-FileStat -info $file_stat
}
Function Get-FilesInFolder($path) {
$items = @()
foreach ($item in (Get-ChildItem -Force -Path $path -ErrorAction SilentlyContinue)) {
if ($item.PSIsContainer -and $recurse) {
if (($item.Attributes -like '*ReparsePoint*' -and $follow) -or ($item.Attributes -notlike '*ReparsePoint*')) {
# File is a link and we want to follow a link OR file is not a link
$items += $item.FullName
$items += Get-FilesInFolder -path $item.FullName
} else {
# File is a link but we don't want to follow a link
$items += $item.FullName
}
} else {
$items += $item.FullName
}
}
$items
}
$paths_to_check = @()
foreach ($path in $paths) {
if (Test-Path $path) {
if ((Get-Item -Force $path).PSIsContainer) {
$paths_to_check += Get-FilesInFolder -path $path
} else {
Fail-Json $result "Argument path $path is a file not a directory"
}
} else {
Fail-Json $result "Argument path $path does not exist cannot get information on"
}
}
$paths_to_check = $paths_to_check | Select -Unique
foreach ($path in $paths_to_check) {
$file = Get-Item -Force -Path $path
$info = Get-FileStat -file $file
$new_examined = $result.examined + 1
$result.examined = $new_examined
if ($info -ne $false) {
$files = $result.Files
$files += $info
$new_matched = $result.matched + 1
$result.matched = $new_matched
$result.files = $files
}
}
Exit-Json $result

View file

@ -0,0 +1,323 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016, Ansible, inc
#
# 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 <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'status': ['preview'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = r'''
---
module: win_find
version_added: "2.3"
short_description: return a list of files based on specific criteria
description:
- Return a list of files based on specified criteria.
- Multiple criteria are AND'd together.
options:
age:
description:
- Select files or folders whose age is equal to or greater than
the specified tim. Use a negative age to find files equal to or
less than the specified time. You can choose seconds, minues,
hours, days or weeks by specifying the first letter of an of
those words (e.g., "1w").
required: false
age_stamp:
description:
- Choose the file property against which we compare C(age). The
default attribute we compare with is last modification time.
required: false
default: mtime
choices: ['atime', 'mtime', 'ctime']
checksum_algorithm:
description:
- Algorithm to determine the checksum of a file. Will throw an error
if the host is unable to use specified algorithm.
required: false
default: sha1
choices: ['md5', 'sha1', 'sha256', 'sha384', 'sha512']
file_type:
description: Type of file to search for
required: false
default: file
choices: ['file', 'directory']
follow:
description:
- Set this to true to follow symlinks in the path. This needs to
be used in conjunction with C(recurse).
required: false
default: false
choices: ['true', 'false']
get_checksum:
description:
- Whether to return a checksum of the file in the return info (default sha1),
use C(checksum_algorithm) to change from the default.
required: false
default: true
choices: ['true', 'false']
hidden:
description: Set this to include hidden files or folders
required: false
default: false
choices: ['true', 'false']
paths:
description:
- List of paths of directories to search for files or folders in.
This can be supplied as a single path or a list of paths.
required: true
patterns:
description:
- One or more (powershell or regex) patterns to compare filenames
with. The type of pattern matching is controlled by C(use_regex)
option. The patterns retrict the list of files or folders to be
returned based on the filenames. For a file to be matched it
only has to match with one pattern in a list provided.
required: false
recurse:
description:
- Will recursively descend into the directory looking for files
or folders
required: false
default: false
choices: ['true', 'false']
size:
description:
- Select files or folders whose size is equal to or greater than
the specified size. Use a negative value to find files equal to
or less than the specified size. You can specify the size with
a suffix of the byte type i.e. kilo = k, mega = m... Size is not
evaluated for symbolic links.
required: false
default: false
use_regex:
description:
- Will set patterns to run as a regex check if true
required: false
default: false
choices: ['true', 'false']
author: "Jordan Borean (@jborean93)"
'''
EXAMPLES = r'''
# Find files in path
- win_find:
paths: D:\temp
# Find hidden files in path
- win_find:
paths: D:\temp
hidden: True
# Find files in multiple paths
- win_find:
paths: ['C:\temp', 'D:\temp']
# Find files in directory while searching recursively
- win_find:
paths: D:\temp
recurse: True
# Find files in directory while following symlinks
- win_find:
paths: D:\temp
recurse: True
follow: True
# Find files with .log and .out extension using powershell wildcards
- win_find:
paths: D:\temp
patterns: ['*.log', '*.out']
# Find files in path based on regex pattern
- win_find:
paths: D:\temp
patterns: "out_\d{8}-\d{6}.log"
# Find files older than 1 day
- win_find:
paths: D:\temp
age: 86400
# Find files older than 1 day based on create time
- win_find:
paths: D:\temp
age: 86400
age_stamp: ctime
# Find files older than 1 day with unit syntax
- win_find:
paths: D:\temp
age: 1d
# Find files newer than 1 hour
- win_find:
paths: D:\temp
age: -3600
# Find files newer than 1 hour with unit syntax
- win_find:
paths: D:\temp
age: -1h
# Find files larger than 1MB
- win_find:
paths: D:\temp
size: 1048576
# Find files larger than 1GB with unit syntax
- win_find:
paths: D:\temp
size: 1g
# Find files smaller than 1MB
- win_find:
paths: D:\temp
size: -1048576
# Find files smaller than 1GB with unit syntax
- win_find:
paths: D:\temp
size: -1g
# Find folders/symlinks in multiple paths
- win_find:
paths: ['C:\temp', 'D:\temp']
file_type: directory
# Find files and return SHA256 checksum of files found
- win_find:
paths: C:\temp
get_checksum: True
checksum_algorithm: sha256
# Find files and do not return the checksum
- win_find:
path: C:\temp
get_checksum: False
'''
RETURN = r'''
changed:
description: Whether anything was chagned
returned: always
type: boolean
sample: True
examined:
description: The number of files/folders that was checked
returned: always
type: int
sample: 10
matched:
description: The number of files/folders that match the criteria
returns: always
type: int
sample: 2
files:
description: Information on the files/folders that match the criteria returned as a list of dictionary elements for each file matched
returned: success
type: dictionary
contains:
attributes:
description: attributes of the file at path in raw form
returned: success, path exists
type: string
sample: "Archive, Hidden"
checksum:
description: The checksum of a file based on checksum_algorithm specified
returned: success, path exists, path is a file, get_checksum == True
type: string
sample: 09cb79e8fc7453c84a07f644e441fd81623b7f98
creationtime:
description: the create time of the file represented in seconds since epoch
returned: success, path exists
type: float
sample: 1477984205.15
extension:
description: the extension of the file at path
returned: success, path exists, path is a file
type: string
sample: ".ps1"
isarchive:
description: if the path is ready for archiving or not
returned: success, path exists
type: boolean
sample: True
isdir:
description: if the path is a directory or not
returned: success, path exists
type: boolean
sample: True
ishidden:
description: if the path is hidden or not
returned: success, path exists
type: boolean
sample: True
islink:
description: if the path is a symbolic link or junction or not
returned: success, path exists
type: boolean
sample: True
isreadonly:
description: if the path is read only or not
returned: success, path exists
type: boolean
sample: True
isshared:
description: if the path is shared or not
returned: success, path exists
type: boolean
sample: True
lastaccesstime:
description: the last access time of the file represented in seconds since epoch
returned: success, path exists
type: float
sample: 1477984205.15
lastwritetime:
description: the last modification time of the file represented in seconds since epoch
returned: success, path exists
type: float
sample: 1477984205.15
lnk_source:
description: the target of the symbolic link, will return null if not a link or the link is broken
return: success, path exists, path is a symbolic link
type: string
sample: C:\temp
owner:
description: the owner of the file
returned: success, path exists
type: string
sample: BUILTIN\Administrators
path:
description: the full absolute path to the file
returned: success, path exists
type: string
sample: BUILTIN\Administrators
sharename:
description: the name of share if folder is shared
returned: success, path exists, path is a directory and isshared == True
type: string
sample: file-share
size:
description: the size in bytes of a file or folder
returned: success, path exists, path is not a link
type: int
sample: 1024
'''

View file

@ -0,0 +1 @@
windows/ci/group/2

View file

@ -0,0 +1 @@
win_find_dir: "{{win_output_dir}}\\win_find"

View file

@ -0,0 +1,9 @@
$path = $args[0]
$attr = $args[1]
$item = Get-Item "$path"
$attributes = $item.Attributes -split ','
If ($attributes -notcontains $attr) {
$attributes += $attr
}
$item.Attributes = $attributes -join ','

View file

@ -0,0 +1,6 @@
$date = Get-Date -Year 2016 -Month 11 -Day 1 -Hour 7 -Minute 10 -Second 5 -Millisecond 0
$item = Get-Item -Path "$($args[0])"
$item.CreationTime = $date
$item.LastAccessTime = $date
$item.LastWriteTime = $date

View file

@ -0,0 +1,7 @@
$share_name = $args[1]
$share_stat = Get-WmiObject -Class Win32_Share -Filter "name='$share_name'"
If ($share_stat) {
$share_stat.Delete()
}
$wmi = [wmiClass] 'Win32_Share'
$wmi.Create($args[0], $share_name, 0)

View file

@ -0,0 +1,2 @@
dependencies:
- prepare_win_tests

View file

@ -0,0 +1,754 @@
---
- name: remove links if they exist as win_file struggles
win_command: cmd.exe /c rmdir "{{item}}"
ignore_errors: True
with_items:
- "{{win_find_dir}}\\nested\\link"
- "{{win_find_dir}}\\broken-link"
- "{{win_find_dir}}\\hard-link-dest\\hard-link.log"
- "{{win_find_dir}}\\junction-link"
- name: ensure the testing directory is cleared before setting up test
win_file:
path: "{{win_find_dir}}"
state: absent
- name: ensure testing directories exist
win_file:
path: "{{item}}"
state: directory
with_items:
- "{{win_find_dir}}\\nested"
- "{{win_find_dir}}\\single"
- "{{win_find_dir}}\\link-dest"
- "{{win_find_dir}}\\link-dest\\sub-link"
- "{{win_find_dir}}\\hard-link-dest"
- "{{win_find_dir}}\\junction-link-dest"
- "{{win_find_dir}}\\broken-link-dest"
- "{{win_find_dir}}\\nested\\sub-nest"
- "{{win_find_dir}}\\shared"
- "{{win_find_dir}}\\shared\\folder"
- "{{win_find_dir}}\\hidden"
- "{{win_find_dir}}\\date"
- name: create empty test files
win_file:
path: "{{item}}"
state: touch
with_items:
- "{{win_find_dir}}\\nested\\file.ps1"
- "{{win_find_dir}}\\nested\\test.ps1"
- "{{win_find_dir}}\\nested\\out.log"
- "{{win_find_dir}}\\nested\\archive.log"
- "{{win_find_dir}}\\nested\\sub-nest\\test.ps1"
- "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt"
- "{{win_find_dir}}\\link-dest\\link.ps1"
- "{{win_find_dir}}\\single\\large.ps1"
- "{{win_find_dir}}\\single\\small.ps1"
- "{{win_find_dir}}\\single\\test.ps1"
- "{{win_find_dir}}\\single\\hidden.ps1"
- "{{win_find_dir}}\\date\\new.ps1"
- "{{win_find_dir}}\\date\\old.ps1"
- "{{win_find_dir}}\\single\\out_20161101-091005.log"
- "{{win_find_dir}}\\hidden\\out_20161101-091005.log"
- "{{win_find_dir}}\\hard-link-dest\\file-abc.log"
- name: populate files with a test string
win_lineinfile:
dest: "{{item.path}}"
line: "{{item.text}}"
with_items:
- { 'path': "{{win_find_dir}}\\nested\\file.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\nested\\test.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\nested\\out.log", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\nested\\archive.log", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\nested\\sub-nest\\test.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\link-dest\\link.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\single\\test.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\single\\hidden.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\date\\new.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\date\\old.ps1", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\single\\out_20161101-091005.log", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\hidden\\out_20161101-091005.log", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\hard-link-dest\\file-abc.log", 'text': 'abcdefg1234567' }
- { 'path': "{{win_find_dir}}\\single\\small.ps1", 'text': "a" }
- { 'path': "{{win_find_dir}}\\date\\new.ps1", 'text': "random text for new date" }
- { 'path': "{{win_find_dir}}\\date\\old.ps1", 'text': "random text for old date" }
- name: populate large text file
win_command: powershell "Set-Content {{win_find_dir}}\\single\\large.ps1 ('abcdefghijklmnopqrstuvwxyz' * 10000)"
- name: create share
script: set_share.ps1 "{{win_find_dir}}\shared\folder" "folder-share"
- name: create links
win_command: cmd.exe /c mklink /{{item.type}} "{{item.source}}" "{{item.target}}"
with_items:
- { type: 'D', source: "{{win_find_dir}}\\nested\\link", target: "{{win_find_dir}}\\link-dest" }
- { type: 'D', source: "{{win_find_dir}}\\broken-link", target: "{{win_find_dir}}\\broken-link-dest" }
- { type: 'H', source: "{{win_find_dir}}\\hard-link-dest\\hard-link.log", target: "{{win_find_dir}}\\hard-link-dest\\file-abc.log" }
- { type: 'J', source: "{{win_find_dir}}\\junction-link", target: "{{win_find_dir}}\\junction-link-dest" }
- name: set modification date on files/folders
script: set_filedate.ps1 "{{item}}"
with_items:
- "{{win_find_dir}}\\nested\\file.ps1"
- "{{win_find_dir}}\\nested\\test.ps1"
- "{{win_find_dir}}\\nested\\out.log"
- "{{win_find_dir}}\\nested\\archive.log"
- "{{win_find_dir}}\\nested\\sub-nest\\test.ps1"
- "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt"
- "{{win_find_dir}}\\link-dest\\link.ps1"
- "{{win_find_dir}}\\single\\large.ps1"
- "{{win_find_dir}}\\single\\small.ps1"
- "{{win_find_dir}}\\single\\test.ps1"
- "{{win_find_dir}}\\single\\hidden.ps1"
- "{{win_find_dir}}\\date\\old.ps1"
- "{{win_find_dir}}\\single\\out_20161101-091005.log"
- "{{win_find_dir}}\\hidden\\out_20161101-091005.log"
- "{{win_find_dir}}\\hard-link-dest\\file-abc.log"
- "{{win_find_dir}}\\nested"
- "{{win_find_dir}}\\single"
- "{{win_find_dir}}\\link-dest"
- "{{win_find_dir}}\\link-dest\\sub-link"
- "{{win_find_dir}}\\hard-link-dest"
- "{{win_find_dir}}\\junction-link-dest"
- "{{win_find_dir}}\\broken-link-dest"
- "{{win_find_dir}}\\nested\\sub-nest"
- "{{win_find_dir}}\\shared"
- "{{win_find_dir}}\\shared\\folder"
- "{{win_find_dir}}\\hidden"
- "{{win_find_dir}}\\date"
- name: set file attributes for test
script: set_attributes.ps1 "{{item.path}}" {{item.attr}}
with_items:
- { 'path': "{{win_find_dir}}\\hidden", 'attr': "Hidden" }
- { 'path': "{{win_find_dir}}\\date", 'attr': "Hidden" }
- { 'path': "{{win_find_dir}}\\nested\\archive.log", 'attr': "Archive" }
- { 'path': "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt", 'attr': "ReadOnly" }
- { 'path': "{{win_find_dir}}\\single\\hidden.ps1", 'attr': "Hidden" }
- name: break the broken link target
win_file:
path: "{{win_find_dir}}\\broken-link-dest"
state: absent
# end test setup
- name: expect failure when not setting paths
win_find:
patterns: a
register: actual
failed_when: "actual.msg != 'Missing required argument: paths'"
- name: expect failure when setting paths to a file
win_find:
paths: "{{win_output_dir}}\\win_find\\single\\large.ps1"
register: actual
failed_when: "actual.msg != 'Argument path {{win_output_dir|regex_replace('\\\\', '\\\\\\\\')}}\\win_find\\single\\large.ps1 is a file not a directory'"
- name: expect failure whe path is set to a non existant folder
win_find:
paths: "{{win_output_dir}}\\win_find\\thisisafakefolder"
register: actual
failed_when: "actual.msg != 'Argument path {{win_output_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\win_find\\\\thisisafakefolder does not exist cannot get information on'"
- name: get files in single directory
win_find:
paths: "{{win_output_dir}}\\win_find\\single"
register: actual
- name: set expected value for files in a single directory
set_fact:
expected:
changed: False
examined: 5
files:
- { isarchive: True,
attributes: Archive,
checksum: f8d100cdcf0e6c1007db2f8dd0b7ee2884df89af,
creationtime: 1477984205,
extension: .ps1,
filename: large.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\large.ps1",
isreadonly: False,
isshared: False,
size: 260002 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .log,
filename: out_20161101-091005.log,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\out_20161101-091005.log",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8,
creationtime: 1477984205,
extension: .ps1,
filename: small.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\small.ps1",
isreadonly: False,
isshared: False,
size: 1 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: test.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\test.ps1",
isreadonly: False,
isshared: False,
size: 14 }
matched: 4
- name: assert actual == expected
assert:
that: "actual == expected"
- name: find hidden files
win_find:
paths: ['{{win_find_dir}}\\single', '{{win_find_dir}}\\nested']
hidden: True
register: actual
- name: set fact for hidden files
set_fact:
expected:
changed: False
examined: 11
files:
- { isarchive: True,
attributes: "Hidden, Archive",
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: hidden.ps1,
ishidden: True,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\hidden.ps1",
isreadonly: False,
isshared: False,
size: 14 }
matched: 1
- name: assert actual == expected
assert:
that: "actual == expected"
- name: find file based on pattern
win_find:
paths: '{{win_find_dir}}\\single'
patterns: ['*.log', 'out_*']
register: actual_pattern
- name: find file based on pattern regex
win_find:
paths: '{{win_find_dir}}\\single'
patterns: "out_\\d{8}-\\d{6}.log"
use_regex: True
register: actual_regex
- name: set fact for pattern files
set_fact:
expected:
changed: False
examined: 5
files:
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .log,
filename: out_20161101-091005.log,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\out_20161101-091005.log",
isreadonly: False,
isshared: False,
size: 14 }
matched: 1
- name: assert actual == expected
assert:
that:
- "actual_pattern == expected"
- "actual_regex == expected"
- name: find files with recurse set
win_find:
paths: "{{win_find_dir}}\\nested"
recurse: True
patterns: "*.ps1"
register: actual
- name: set expected value for files in a nested directory
set_fact:
expected:
changed: False
examined: 8
files:
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: test.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\sub-nest\\test.ps1",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: file.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\file.ps1",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: test.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\test.ps1",
isreadonly: False,
isshared: False,
size: 14 }
matched: 3
- name: assert actual == expected
assert:
that: "actual == expected"
- name: find files with recurse set and follow links
win_find:
paths: "{{win_find_dir}}\\nested"
recurse: True
follow: True
patterns: "*.ps1"
register: actual
- name: set expected value for files in a nested directory while following links
set_fact:
expected:
changed: False
examined: 10
files:
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: link.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\link\\link.ps1",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: test.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\sub-nest\\test.ps1",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: file.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\file.ps1",
isreadonly: False,
isshared: False,
size: 14 }
- { isarchive: True,
attributes: Archive,
checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
creationtime: 1477984205,
extension: .ps1,
filename: test.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\nested\\test.ps1",
isreadonly: False,
isshared: False,
size: 14 }
matched: 4
- name: assert actual == expected
assert:
that: "actual == expected"
- name: find directories
win_find:
paths: "{{win_find_dir}}\\link-dest"
file_type: directory
register: actual
- name: set expected fact for directories with recurse and follow
set_fact:
expected:
changed: False
examined: 2
files:
- { isarchive: False,
attributes: Directory,
creationtime: 1477984205,
filename: sub-link,
ishidden: False,
isdir: True,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\link-dest\\sub-link",
isreadonly: False,
isshared: False,
size: 0 }
matched: 1
- name: assert actual == expected
assert:
that: "actual == expected"
- name: find directories recurse and follow with a broken link
win_find:
paths: "{{win_find_dir}}"
file_type: directory
recurse: True
follow: True
register: actual
- name: check directory count with recurse and follow is correct
assert:
that:
- "actual.examined == 33"
- "actual.matched == 13"
- "actual.files[0].filename == 'broken-link'"
- "actual.files[0].islink == True"
- "actual.files[2].filename == 'junction-link'"
- "actual.files[2].islink == True"
- "actual.files[2].lnk_source == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\junction-link-dest'"
- "actual.files[7].filename == 'link'"
- "actual.files[7].islink == True"
- "actual.files[7].lnk_source == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\link-dest'"
- "actual.files[11].filename == 'folder'"
- "actual.files[11].islink == False"
- "actual.files[11].isshared == True"
- "actual.files[11].sharename == 'folder-share'"
- name: filter files by size without byte specified
win_find:
paths: "{{win_find_dir}}\\single"
size: 260002
register: actual_without_byte
- name: filter files by size with byte specified
win_find:
paths: "{{win_find_dir}}\\single"
size: 253k
register: actual_with_byte
- name: set expected fact for files by size
set_fact:
expected:
changed: False
examined: 5
files:
- { isarchive: True,
attributes: Archive,
checksum: f8d100cdcf0e6c1007db2f8dd0b7ee2884df89af,
creationtime: 1477984205,
extension: ".ps1",
filename: large.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\large.ps1",
isreadonly: False,
isshared: False,
size: 260002 }
matched: 1
- name: assert actual == expected
assert:
that:
- "actual_without_byte == expected"
- "actual_with_byte == expected"
- name: filter files by size (less than) without byte specified
win_find:
paths: "{{win_find_dir}}\\single"
size: -4
register: actual_without_byte
- name: filter files by size (less than) with byte specified
win_find:
paths: "{{win_find_dir}}\\single"
size: -4b
register: actual_with_byte
- name: set expected fact for files by size (less than)
set_fact:
expected:
changed: False
examined: 5
files:
- { isarchive: True,
attributes: Archive,
checksum: 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8,
creationtime: 1477984205,
extension: ".ps1",
filename: small.ps1,
ishidden: False,
isdir: False,
islink: False,
lastaccesstime: 1477984205,
lastwritetime: 1477984205,
owner: BUILTIN\Administrators,
path: "{{win_find_dir}}\\single\\small.ps1",
isreadonly: False,
isshared: False,
size: 1 }
matched: 1
- name: assert actual == expected
assert:
that:
- "actual_without_byte == expected"
- "actual_with_byte == expected"
# For dates we cannot assert against expected as the times change, this is a poor mans attempt at testing
- name: filter files by age without unit specified
win_find:
paths: "{{win_find_dir}}\\date"
age: 3600
register: actual_without_unit
- name: filter files by age with unit specified
win_find:
paths: "{{win_find_dir}}\\date"
age: 1h
register: actual_with_unit
- name: assert dates match each other
assert:
that:
- "actual_without_unit == actual_with_unit"
- "actual_without_unit.matched == 1"
- "actual_without_unit.files[0].checksum == '7454f04e3ac587f711a416f4edf26507255e0a2e'"
- "actual_without_unit.files[0].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\new.ps1'"
- name: filter files by age (older than) without unit specified
win_find:
paths: "{{win_find_dir}}\\date"
age: -1
register: actual_without_unit
- name: filter files by age (older than) without unit specified
win_find:
paths: "{{win_find_dir}}\\date"
age: -1s
register: actual_with_unit
- name: assert dates match each other
assert:
that:
- "actual_without_unit == actual_with_unit"
- "actual_without_unit.matched == 2"
- "actual_without_unit.files[0].checksum == '7454f04e3ac587f711a416f4edf26507255e0a2e'"
- "actual_without_unit.files[0].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\new.ps1'"
- "actual_without_unit.files[1].checksum == '031a04ecc76f794d7842651de732075dec6fef04'"
- "actual_without_unit.files[1].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\old.ps1'"
- name: get list of files with md5 checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
checksum_algorithm: md5
register: actual_md5_checksum
- name: assert md5 checksum value
assert:
that:
- "actual_md5_checksum.files[0].checksum == 'd1713d0f1d2e8fae230328d8fd59de01'"
- name: get list of files with sha1 checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
checksum_algorithm: sha1
register: actual_sha1_checksum
- name: assert sha1 checksum value
assert:
that:
- "actual_sha1_checksum.files[0].checksum == '8df33cee3325596517df5bb5aa980cf9c5c1fda3'"
- name: get list of files with sha256 checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
checksum_algorithm: sha256
register: actual_sha256_checksum
- name: assert sha256 checksum value
assert:
that:
- "actual_sha256_checksum.files[0].checksum == 'c20d2eba7ffda0079812721b6f4e4e109e2f0c5e8cc3d1273a060df6f7d9f339'"
- name: get list of files with sha384 checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
checksum_algorithm: sha384
register: actual_sha384_checksum
- name: assert sha384 checksum value
assert:
that:
- "actual_sha384_checksum.files[0].checksum == 'aed515eb216b9c7009ae8c4680f46c1e22004528b231aa0482a8587543bca47d3504e9f77e884eb2d11b2f9f5dc01651'"
- name: get list of files with sha512 checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
checksum_algorithm: sha512
register: actual_sha512_checksum
- name: assert sha512 checksum value
assert:
that:
- "actual_sha512_checksum.files[0].checksum == '05abf64a68c4731699c23b4fc6894a36646fce525f3c96f9cf743b5d0c3bfd933dad0e95e449e3afe1f74d534d69a53b8f46cf835763dd42915813c897b02b87'"
- name: get list of files without checksum
win_find:
paths: "{{win_find_dir}}\\single"
patterns: test.ps1
get_checksum: False
register: actual_no_checksum
- name: assert no checksum is returned
assert:
that:
- "actual_no_checksum.files[0].checksum is undefined"
- name: check if broken symbolic link exists
win_stat:
path: "{{win_find_dir}}\\broken-link"
register: broken_link_exists
- name: delete broken symbolic link if it exists
win_command: cmd.exe /c rmdir {{win_find_dir}}\broken-link
when: broken_link_exists.stat.exists
- name: check if junction symbolic link exists
win_stat:
path: "{{win_find_dir}}\\junction-link"
register: junction_link_exists
- name: delete junction symbolic link if it exists
win_command: cmd.exe /c rmdir {{win_find_dir}}\junction-link
when: junction_link_exists.stat.exists
- name: check if nested symbolic link exists
win_stat:
path: "{{win_find_dir}}\\nested\\link"
register: nested_link_exists
- name: delete nested symbolic link if it exists
win_command: cmd.exe /c rmdir {{win_find_dir}}\nested\link
when: nested_link_exists.stat.exists
- name: remove testing folder
win_file:
path: "{{win_find_dir}}"
state: absent

View file

@ -7,6 +7,7 @@
- { role: win_template, tags: test_win_template }
- { role: win_lineinfile, tags: test_win_lineinfile }
- { role: win_stat, tags: test_win_stat }
- { role: win_find, tags: test_win_find }
- { role: win_get_url, tags: test_win_get_url }
- { role: win_msi, tags: test_win_msi }
- { role: win_package, tags: test_win_package }