mirror of https://github.com/ansible/ansible.git
add win_wait_for_process (#40404)
* add win_wait_for_process * try working on additional filter options and fix derps * improve process name matching: support arrays. support pre and post wait * use CIM instead of WIM. requre exact contain process binary extension * updates to metadata * fix errors in waiting for process to start * validate process min count for absent state. fix typo * fix bug if only one processes is detected * address GitHub commentspull/41101/merge
parent
03261b3053
commit
d5d2c80779
@ -0,0 +1,162 @@
|
|||||||
|
#!powershell
|
||||||
|
# This file is part of Ansible
|
||||||
|
|
||||||
|
# Copyright (c) 2017 Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
#Requires -Module Ansible.ModuleUtils.Legacy
|
||||||
|
#Requires -Module Ansible.ModuleUtils.FileUtil
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$params = Parse-Args -arguments $args -supports_check_mode $true
|
||||||
|
|
||||||
|
$process_name_exact = Get-AnsibleParam -obj $params -name "process_name_exact" -type "list"
|
||||||
|
$process_name_pattern = Get-AnsibleParam -obj $params -name "process_name_pattern" -type "str"
|
||||||
|
$process_id = Get-AnsibleParam -obj $params -name "pid" -type "int" -default 0 #pid is a reserved variable in PowerShell. use process_id instead.
|
||||||
|
$owner = Get-AnsibleParam -obj $params -name "owner" -type "str"
|
||||||
|
$sleep = Get-AnsibleParam -obj $params -name "sleep" -type "int" -default 1
|
||||||
|
$pre_wait_delay = Get-AnsibleParam -obj $params -name "pre_wait_delay" -type "int" -default 0
|
||||||
|
$post_wait_delay = Get-AnsibleParam -obj $params -name "post_wait_delay" -type "int" -default 0
|
||||||
|
$process_min_count = Get-AnsibleParam -obj $params -name "process_min_count" -type "int" -default 1
|
||||||
|
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
|
||||||
|
$timeout = Get-AnsibleParam -obj $params -name "timeout" -type "int" -default 300
|
||||||
|
|
||||||
|
$result = @{
|
||||||
|
changed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# validate the input
|
||||||
|
if ($state -eq "absent" -and $sleep -ne 1)
|
||||||
|
{
|
||||||
|
Add-Warning $result "sleep parameter has no effect when waiting for a process to stop."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($state -eq "absent" -and $process_min_count -ne 1)
|
||||||
|
{
|
||||||
|
Add-Warning $result "process_min_count parameter has no effect when waiting for a process to stop."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($process_name_exact -or $process_name_pattern) -and $process_id)
|
||||||
|
{
|
||||||
|
Fail-json $result "process_id may not be used with process_name_exact or process_name_pattern."
|
||||||
|
}
|
||||||
|
if ($process_name_exact -and $process_name_pattern)
|
||||||
|
{
|
||||||
|
Fail-json $result "process_name_exact and process_name_pattern may not be used at the same time."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not ($process_name_exact -or $process_name_pattern -or $process_id -or $owner))
|
||||||
|
{
|
||||||
|
Fail-json $result "at least one of: process_name_exact, process_name_pattern, process_id, or owner must be supplied."
|
||||||
|
}
|
||||||
|
|
||||||
|
$module_start = Get-Date
|
||||||
|
|
||||||
|
#Get-Process doesn't actually return a UserName value, so get it from WMI.
|
||||||
|
Function Get-ProcessMatchesFilter {
|
||||||
|
[cmdletbinding()]
|
||||||
|
Param(
|
||||||
|
[String]
|
||||||
|
$Owner,
|
||||||
|
$ProcessNameExact,
|
||||||
|
$ProcessNamePattern,
|
||||||
|
[int]
|
||||||
|
$ProcessId
|
||||||
|
)
|
||||||
|
|
||||||
|
$CIMProcesses = Get-CimInstance Win32_Process
|
||||||
|
foreach ($CIMProcess in $CIMProcesses)
|
||||||
|
{
|
||||||
|
$include = $true
|
||||||
|
if(-not [String]::IsNullOrEmpty($ProcessNamePattern))
|
||||||
|
{
|
||||||
|
#if a process name was specified in the filter, validate that here.
|
||||||
|
$include = $include -and ($CIMProcess.ProcessName -match $ProcessNamePattern)
|
||||||
|
}
|
||||||
|
if($ProcessNameExact -is [Array] -or (-not [String]::IsNullOrEmpty($ProcessNameExact)))
|
||||||
|
{
|
||||||
|
#if a process name was specified in the filter, validate that here.
|
||||||
|
if ($ProcessNameExact -is [Array] )
|
||||||
|
{
|
||||||
|
$include = $include -and ($ProcessNameExact -contains $CIMProcess.ProcessName)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$include = $include -and ($ProcessNameExact -eq $CIMProcess.ProcessName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ProcessId -and $ProcessId -ne 0)
|
||||||
|
{
|
||||||
|
# if a PID was specified in the filger, validate that here.
|
||||||
|
$include = $include -and ($CIMProcess.ProcessId -eq $ProcessId)
|
||||||
|
}
|
||||||
|
if (-not [String]::IsNullOrEmpty($Owner) )
|
||||||
|
{
|
||||||
|
# if an owner was specified in the filter, validate that here.
|
||||||
|
$include = $include -and ($($(Invoke-CimMethod -InputObject $CIMProcess -MethodName GetOwner).User) -eq $Owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($include)
|
||||||
|
{
|
||||||
|
$CIMProcess | Select-Object -Property ProcessId, ProcessName, @{name="Owner";Expression={$($(Invoke-CimMethod -InputObject $CIMProcess -MethodName GetOwner).User)}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -Seconds $pre_wait_delay
|
||||||
|
if ($state -eq "present" ) {
|
||||||
|
#wait for a process to start
|
||||||
|
$Processes = @()
|
||||||
|
$attempts = 0
|
||||||
|
Do {
|
||||||
|
if (((Get-Date) - $module_start).TotalSeconds -gt $timeout)
|
||||||
|
{
|
||||||
|
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
|
||||||
|
Fail-Json $result "timeout while waiting for $process_name to start. waited $timeout seconds"
|
||||||
|
}
|
||||||
|
|
||||||
|
$Processes = Get-ProcessMatchesFilter -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
|
||||||
|
Start-Sleep -Seconds $sleep
|
||||||
|
$attempts ++
|
||||||
|
$ProcessCount = $null
|
||||||
|
if ($Processes -is [array]) {
|
||||||
|
$ProcessCount = $Processes.count
|
||||||
|
}
|
||||||
|
elseif ($null -ne $Processes) {
|
||||||
|
$ProcessCount = 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ProcessCount = 0
|
||||||
|
}
|
||||||
|
} While ($ProcessCount -lt $process_min_count)
|
||||||
|
|
||||||
|
if ($attempts -gt 0)
|
||||||
|
{
|
||||||
|
$result.changed = $true
|
||||||
|
}
|
||||||
|
$result.matched_processess = $Processes
|
||||||
|
}
|
||||||
|
elseif ($state -eq "absent") {
|
||||||
|
#wait for a process to stop
|
||||||
|
$Processes = Get-ProcessMatchesFilter -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
|
||||||
|
$result.matched_processes = $Processes
|
||||||
|
$ProcessCount = $(if ($Processes -is [array]) { $Processes.count } elseif ($Processes){ 1 } else {0})
|
||||||
|
if ($ProcessCount -gt 0 )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Wait-Process -Id $($Processes | Select-Object -ExpandProperty ProcessId) -Timeout $timeout -ErrorAction Stop
|
||||||
|
$result.changed = $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
|
||||||
|
Fail-Json $result "$($_.Exception.Message). timeout while waiting for $process_name to stop. waited $timeout seconds"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$result.changed = $false
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Start-Sleep -Seconds $post_wait_delay
|
||||||
|
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
|
||||||
|
Exit-Json $result
|
@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright: (c) 2017, Ansible Project
|
||||||
|
# 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_wait_for_process
|
||||||
|
version_added: '2.7'
|
||||||
|
short_description: Waits for a process to exist or not exist before continuing.
|
||||||
|
description:
|
||||||
|
- Waiting for a process to start or stop is useful when Windows services
|
||||||
|
behave poorly and do not enumerate external dependencies in their
|
||||||
|
manifest.
|
||||||
|
options:
|
||||||
|
process_name_exact:
|
||||||
|
description:
|
||||||
|
- The name of the process(es) for which to wait.
|
||||||
|
- Must inclue the file extension of the process binary (.exe)
|
||||||
|
process_name_pattern:
|
||||||
|
description:
|
||||||
|
- RegEx pattern matching desired process(es)
|
||||||
|
sleep:
|
||||||
|
description:
|
||||||
|
- Number of seconds to sleep between checks.
|
||||||
|
- Only applies when waiting for a process to start. Waiting for a process to start
|
||||||
|
does not have a native non-polling mechanism. Waiting for a stop uses native PowerShell
|
||||||
|
and does not require polling.
|
||||||
|
default: 1
|
||||||
|
process_min_count:
|
||||||
|
description:
|
||||||
|
- Minimum number of process matching the supplied pattern to satisfy C(present) condition.
|
||||||
|
- Only applies to C(present).
|
||||||
|
default: 1
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- When checking for a running process C(present) will block execution
|
||||||
|
until the process exists, or until the timeout has been reached.
|
||||||
|
C(absent) will block execution untile the processs no longer exists,
|
||||||
|
or until the timeout has been reached.
|
||||||
|
- When waiting for C(present), the module will return changed only if
|
||||||
|
the process was not present on the initial check but became present on
|
||||||
|
subsequent checks.
|
||||||
|
- If, while waiting for C(absent), new processes matching the supplied
|
||||||
|
pattern are started, these new processes will not be included in the
|
||||||
|
action.
|
||||||
|
default: present
|
||||||
|
choices: [ absent, present ]
|
||||||
|
timeout:
|
||||||
|
description:
|
||||||
|
- The maximum number of seconds to wait for a for a process to start or stop
|
||||||
|
before erroring out.
|
||||||
|
default: 300
|
||||||
|
author:
|
||||||
|
- Charles Crossan (@crossan007)
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
- name: Wait 300 seconds for all Oracle VirtualBox processes to stop. (VBoxHeadless, VirtualBox, VBoxSVC)
|
||||||
|
win_wait_for_process:
|
||||||
|
process_name: "v(irtual)?box(headless|svc)?"
|
||||||
|
state: absent
|
||||||
|
timeout: 500
|
||||||
|
|
||||||
|
|
||||||
|
- name: Wait 300 seconds for 3 instances of cmd to start, waiting 5 seconds between each check
|
||||||
|
win_wait_for_process:
|
||||||
|
process_name: "cmd\\.exe"
|
||||||
|
state: present
|
||||||
|
timeout: 500
|
||||||
|
sleep: 5
|
||||||
|
process_min_count: 3
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
elapsed:
|
||||||
|
description: The elapsed seconds between the start of poll and the end of the
|
||||||
|
module.
|
||||||
|
returned: always
|
||||||
|
type: float
|
||||||
|
sample: 3.14159265
|
||||||
|
changed:
|
||||||
|
description: True if a process was started or stopped during the module execution.
|
||||||
|
returned: always
|
||||||
|
type: bool
|
||||||
|
matched_processes:
|
||||||
|
description: Count of processes stopped or started.
|
||||||
|
returned: always
|
||||||
|
type: int
|
||||||
|
'''
|
Loading…
Reference in New Issue