win_hosts to use Ansible.Basic CSharp Util and better diff support (#58600)

* switch win_hosts to use csharp util

* update win_hosts doc to match doc guide

* changed linking format for option values
This commit is contained in:
Micah Hunsberger 2019-07-21 19:08:36 -04:00 committed by Jordan Borean
parent 015119df8c
commit 74598b212e
2 changed files with 59 additions and 92 deletions

View file

@ -3,36 +3,39 @@
# Copyright: (c) 2018, Micah Hunsberger (@mhunsber)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
#AnsibleRequires -CSharpUtil Ansible.Basic
Set-StrictMode -Version 2
$ErrorActionPreference = "Stop"
$params = Parse-Args -arguments $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
$spec = @{
options = @{
state = @{ type = "str"; choices = "absent", "present"; default = "present" }
aliases = @{ type = "list"; elements = "str" }
canonical_name = @{ type = "str" }
ip_address = @{ type = "str" }
action = @{ type = "str"; choices = "add", "remove", "set"; default = "set" }
}
required_if = @(,@( "state", "present", @("canonical_name", "ip_address")))
supports_check_mode = $true
}
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present"
$aliases = Get-AnsibleParam -obj $params -name "aliases" -type "list" -failifempty $false
$canonical_name = Get-AnsibleParam -obj $params -name "canonical_name" -type "str" -failifempty ($state -eq 'present')
$ip_address = Get-AnsibleParam -obj $params -name "ip_address" -type "str" -default "" -failifempty ($state -eq 'present')
$action = Get-AnsibleParam -obj $params -name "action" -type "str" -default "set" -validateset "add","remove","set"
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$state = $module.Params.state
$aliases = $module.Params.aliases
$canonical_name = $module.Params.canonical_name
$ip_address = $module.Params.ip_address
$action = $module.Params.action
$tmp = [ipaddress]::None
if($ip_address -and -not [ipaddress]::TryParse($ip_address, [ref]$tmp)){
Fail-Json -obj @{} -message "win_hosts: Argument ip_address needs to be a valid ip address, but was $ip_address"
$module.FailJson("win_hosts: Argument ip_address needs to be a valid ip address, but was $ip_address")
}
$ip_address_type = $tmp.AddressFamily
$hosts_file = Get-Item -LiteralPath "$env:SystemRoot\System32\drivers\etc\hosts"
$result = @{
changed = $false
diff = @{
prepared = ""
}
}
Function Get-CommentIndex($line) {
$c_index = $line.IndexOf('#')
if($c_index -lt 0) {
@ -79,30 +82,14 @@ Function Find-HostName($line, $name) {
}
Function Remove-HostEntry($list, $idx) {
$result.changed = $true
$removed = $false
if($diff_mode) {
$result.diff.prepared += "`n-[$($list[$idx])]`n"
}
if(-not $check_mode) {
$list.RemoveAt($idx)
$removed = $true
}
return $removed
$module.Result.changed = $true
$list.RemoveAt($idx)
}
Function Add-HostEntry($list, $cname, $aliases, $ip) {
$result.changed = $true
$module.Result.changed = $true
$line = "$ip $cname $($aliases -join ' ')"
if($diff_mode) {
$result.diff.prepared += "`n+[$line]`n"
}
if(-not $check_mode) {
$list.Add($line) | Out-Null
}
$list.Add($line) | Out-Null
}
Function Remove-HostnamesFromEntry($list, $idx, $aliases) {
@ -115,13 +102,8 @@ Function Remove-HostnamesFromEntry($list, $idx, $aliases) {
$line = $line.Remove($match.Index + 1, $match.Length -1)
# was this the last alias? (check for space characters after trimming)
if($line.Substring(0,(Get-CommentIndex -line $line)).Trim() -inotmatch "\s") {
if($diff_mode){
$result.diff.prepared += "`n-[$($list[$idx])]`n"
}
if(-not $check_mode) {
$list.RemoveAt($idx)
$line_removed = $true
}
$list.RemoveAt($idx)
$line_removed = $true
# we're done
return @{
line_removed = $line_removed
@ -130,13 +112,8 @@ Function Remove-HostnamesFromEntry($list, $idx, $aliases) {
}
}
if($line -ne $list[$idx]){
$result.changed = $true
if($diff_mode) {
$result.diff.prepared += "`n-[$($list[$idx])]`n+[$line]`n"
}
if(-not $check_mode) {
$list[$idx] = $line
}
$module.Result.changed = $true
$list[$idx] = $line
}
return @{
line_removed = $line_removed
@ -153,19 +130,15 @@ Function Add-AliasesToEntry($list, $idx, $aliases) {
}
}
if($line -ne $list[$idx]){
$result.changed = $true
if($diff_mode) {
$result.diff.prepared += "`n-[$($list[$idx])]`n+[$line]`n"
}
if(-not $check_mode) {
$list[$idx] = $line
}
$module.Result.changed = $true
$list[$idx] = $line
}
}
$hosts_lines = New-Object System.Collections.ArrayList
Get-Content -LiteralPath $hosts_file.FullName | ForEach-Object { $hosts_lines.Add($_) } | Out-Null
$module.Diff.before = ($hosts_lines -join "`n") + "`n"
if ($state -eq 'absent') {
# go through and remove canonical_name and ip
@ -200,11 +173,9 @@ if($state -eq 'present') {
$aliases_to_remove = @()
if($entry_parts.ip_address -eq $ip_address) {
if($entry_parts.canonical_name -eq $canonical_name) {
# don't need to worry about line being removed since canonical_name is present
$entry_idx = $idx
if($action -eq 'set') {
# remove the entry's aliases that are not in $aliases
$aliases_to_remove = $entry_parts.aliases | Where-Object { $aliases -notcontains $_ }
} elseif($action -eq 'remove') {
$aliases_to_remove = $aliases
@ -220,41 +191,31 @@ if($state -eq 'present') {
# this is not the ip_address we are looking for
if ($ip_address_type -eq $entry_parts.ip_type) {
if ($entry_parts.canonical_name -eq $canonical_name) {
# remove the entry
if (Remove-HostEntry -list $hosts_lines -idx $idx){
# keep index correct if we removed the line
$idx = $idx - 1
}
Remove-HostEntry -list $hosts_lines -idx $idx
$idx = $idx - 1
if ($action -ne "set") {
# keep old aliases intact
$aliases_to_keep += $entry_parts.aliases | Where-Object { ($aliases + $aliases_to_keep + $canonical_name) -notcontains $_ }
}
} elseif ($action -eq "remove") {
# just remove canonical_name. user may want alias(es) mapped to this canonical name
$aliases_to_remove = $canonical_name
} elseif ($aliases -contains $entry_parts.canonical_name) {
# remove the entry
if (Remove-HostEntry -list $hosts_lines -idx $idx) {
# keep index correct if we removed the line
$idx = $idx - 1
}
Remove-HostEntry -list $hosts_lines -idx $idx
$idx = $idx - 1
if ($action -eq "add") {
# keep old aliases intact
$aliases_to_keep += $entry_parts.aliases | Where-Object { ($aliases + $aliases_to_keep + $canonical_name) -notcontains $_ }
}
} else {
# ensure canonical_name and aliases removed from this entry
$aliases_to_remove = $aliases + $canonical_name
}
} else {
# Just ignore if the types don't match.
# TODO: Better ipv6 support. There is odd behavior for when an alias can be used for both ipv6 and ipv4
}
}
if($aliases_to_remove) {
if((Remove-HostnamesFromEntry -list $hosts_lines -idx $idx -aliases $aliases_to_remove).line_removed) {
# keep index correct if we removed the line
$idx = $idx - 1
}
}
@ -263,14 +224,11 @@ if($state -eq 'present') {
}
if($entry_idx -ge 0) {
# we found the entry
$aliases_to_add = @()
$entry_parts = Get-HostEntryParts -line $hosts_lines[$entry_idx]
if($action -eq 'remove') {
# just preserve any previously removed aliases
$aliases_to_add = $aliases_to_keep | Where-Object { $entry_parts.aliases -notcontains $_ }
} else {
# we want to add provided aliases and previously removed aliases that are not already in the list
$aliases_to_add = ($aliases + $aliases_to_keep) | Where-Object { $entry_parts.aliases -notcontains $_ }
}
@ -291,8 +249,9 @@ if($state -eq 'present') {
}
}
if( $result.changed -and -not $check_mode ) {
$module.Diff.after = ($hosts_lines -join "`n") + "`n"
if( $module.Result.changed -and -not $module.CheckMode ) {
Set-Content -LiteralPath $hosts_file.FullName -Value $hosts_lines
}
Exit-Json $result
$module.ExitJson()

View file

@ -18,62 +18,70 @@ version_added: '2.8'
short_description: Manages hosts file entries on Windows.
description:
- Manages hosts file entries on Windows.
- Maps IPv4 or IPv6 addresses to canonical names
- Adds, removes, or sets cname records for ip and hostname pairs
- Modifies %windir%\system32\drivers\etc\hosts.
- Maps IPv4 or IPv6 addresses to canonical names.
- Adds, removes, or sets cname records for ip and hostname pairs.
- Modifies %windir%\\system32\\drivers\\etc\\hosts.
options:
state:
description:
- Whether the entry should be present or absent.
- If only C(canonical_name) is provided when C(state=absent), then
- If only I(canonical_name) is provided when C(state=absent), then
all hosts entries with the canonical name of I(canonical_name)
will be removed.
- If only C(ip_address) is provided when C(state=absent), then all
- If only I(ip_address) is provided when C(state=absent), then all
hosts entries with the ip address of I(ip_address) will be removed.
- If C(ip_address) and C(canonical_name) are both omitted when
- If I(ip_address) and I(canonical_name) are both omitted when
C(state=absent), then all hosts entries will be removed.
choices:
- absent
- present
default: present
type: str
canonical_name:
description:
- A canonical name for the host entry.
- required for C(state=present).
type: str
ip_address:
description:
- The ip address for the host entry.
- Can be either IPv4 (A record) or IPv6 (AAAA record).
- Required for C(state=present).
type: str
aliases:
description:
- A list of additional names (cname records) for the host entry.
- Only applicable when C(state=present).
type: list
action:
choices:
- add
- remove
- set
description:
- Controls the behavior of C(aliases).
- Controls the behavior of I(aliases).
- Only applicable when C(state=present).
- If C(add), each alias in I(aliases) will be added to the host entry.
- If C(set), each alias in I(aliases) will be added to the host entry,
and other aliases will be removed from the entry.
default: set
type: str
author:
- Micah Hunsberger (@mhunsber)
notes:
- Each canonical name can only be mapped to one IPv4 and one IPv6 address.
If C(canonical_name) is provided with C(state=present) and is found
If I(canonical_name) is provided with C(state=present) and is found
to be mapped to another IP address that is the same type as, but unique
from C(ip_address), then C(canonical_name) and all C(aliases) will
from I(ip_address), then I(canonical_name) and all I(aliases) will
be removed from the entry and added to an entry with the provided IP address.
- Each alias can only be mapped to one canonical name. If C(aliases) is provided
- Each alias can only be mapped to one canonical name. If I(aliases) is provided
with C(state=present) and an alias is found to be mapped to another canonical
name, then the alias will be removed from the entry and added to or removed
from (based on I(action)) an entry with the provided canonical name.
- See also M(win_template), M(win_file), M(win_copy)
name, then the alias will be removed from the entry and either added to or removed
from (depending on I(action)) an entry with the provided canonical name.
seealso:
- module: win_template
- module: win_file
- module: win_copy
'''
EXAMPLES = r'''