From eda6a6baee56efa68576ccc1adf06e13e8c3dc02 Mon Sep 17 00:00:00 2001 From: Daniele Lazzari Date: Wed, 7 Jun 2017 19:25:50 +0200 Subject: [PATCH] New module: Add module to add/remove a network static route on windows (windows/win_route) (#23405) * adds win_route windows module * fix documentation indent * fix documentation pep8 issues * fix documentation issues * genaral code review * changed 'supported_by' in doc, supports_check_mode removed * use of powershell cmdlet instead of route command, destnation in CIDR format, adds check mode support * adds win_psmodule module * documentation review * documentation review * removed files accidentatlly pushed * add integration tests, add return in documentation * add conditional statement in test, add os requirementes in documentation * fix documentation * fix os check in tests --- lib/ansible/modules/windows/win_route.ps1 | 118 ++++++++++++++++++ lib/ansible/modules/windows/win_route.py | 78 ++++++++++++ test/integration/targets/win_route/aliases | 1 + .../targets/win_route/defaults/main.yml | 3 + .../targets/win_route/files/Test-Os.ps1 | 13 ++ .../targets/win_route/tasks/main.yml | 29 +++++ .../targets/win_route/tasks/tests.yml | 74 +++++++++++ 7 files changed, 316 insertions(+) create mode 100644 lib/ansible/modules/windows/win_route.ps1 create mode 100644 lib/ansible/modules/windows/win_route.py create mode 100644 test/integration/targets/win_route/aliases create mode 100644 test/integration/targets/win_route/defaults/main.yml create mode 100644 test/integration/targets/win_route/files/Test-Os.ps1 create mode 100644 test/integration/targets/win_route/tasks/main.yml create mode 100644 test/integration/targets/win_route/tasks/tests.yml diff --git a/lib/ansible/modules/windows/win_route.ps1 b/lib/ansible/modules/windows/win_route.ps1 new file mode 100644 index 0000000000..44e38ed971 --- /dev/null +++ b/lib/ansible/modules/windows/win_route.ps1 @@ -0,0 +1,118 @@ +#!powershell +# This file is part of Ansible +# +# Copyright 2016, Daniele Lazzari +# +# 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 + +# win_route (Add or remove a network static route) + +$params = Parse-Args $args -supports_check_mode $true + +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false +$dest = Get-AnsibleParam -obj $params -name "destination" -type "str" -failifempty $true +$gateway = Get-AnsibleParam -obj $params -name "gateway" -type "str" +$metric = Get-AnsibleParam -obj $params -name "metric" -type "int" -default 1 +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateSet "present","absent" +$result = @{ + "changed" = $false + "output" = "" + } + +Function Add-Route { + Param ( + [Parameter(Mandatory=$true)] + [string]$Destination, + [Parameter(Mandatory=$true)] + [string]$Gateway, + [Parameter(Mandatory=$true)] + [int]$Metric, + [Parameter(Mandatory=$true)] + [bool]$CheckMode + ) + + + $IpAddress = $Destination.split('/')[0] + + # Check if the static route is already present + $Route = Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '$($IpAddress)'" + if (!($Route)){ + try { + # Find Interface Index + $InterfaceIndex = Find-NetRoute -RemoteIPAddress $IpAddress | Select -First 1 -ExpandProperty InterfaceIndex + + # Add network route + New-NetRoute -DestinationPrefix $Destination -NextHop $Gateway -InterfaceIndex $InterfaceIndex -RouteMetric $Metric -ErrorAction Stop -WhatIf:$CheckMode|out-null + $result.changed = $true + $result.output = "Route added" + + } + catch { + $ErrorMessage = $_.Exception.Message + Fail-Json $result $ErrorMessage + } + } + else { + $result.output = "Static route already exists" + } + +} + +Function Remove-Route { + Param ( + [Parameter(Mandatory=$true)] + [string]$Destination, + [bool]$CheckMode + ) + $IpAddress = $Destination.split('/')[0] + $Route = Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '$($IpAddress)'" + if ($Route){ + try { + + Remove-NetRoute -DestinationPrefix $Destination -Confirm:$false -ErrorAction Stop -WhatIf:$CheckMode + $result.changed = $true + $result.output = "Route removed" + } + catch { + $ErrorMessage = $_.Exception.Message + Fail-Json $result $ErrorMessage + } + } + else { + $result.output = "No route to remove" + } + +} + +# Set gateway if null +if(!($gateway)){ + $gateway = "0.0.0.0" +} + + +if ($state -eq "present"){ + + Add-Route -Destination $dest -Gateway $gateway -Metric $metric -CheckMode $check_mode + +} +else { + + Remove-Route -Destination $dest -CheckMode $check_mode + +} + +Exit-Json $result \ No newline at end of file diff --git a/lib/ansible/modules/windows/win_route.py b/lib/ansible/modules/windows/win_route.py new file mode 100644 index 0000000000..192de5bfb0 --- /dev/null +++ b/lib/ansible/modules/windows/win_route.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Daniele Lazzari +# +# 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 . + +# this is a windows documentation stub. actual code lives in the .ps1 +# file of the same name + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: win_route +version_added: "2.4" +short_description: Add or remove a static route. +description: + - Add or remove a static route. +options: + destination: + description: + - Destination IP address in CIDR format (ip address/prefix lenght) + required: true + gateway: + description: + - Gateway used by the static route. If gateway is not provided it will be set to "0.0.0.0" + metric: + description: + - Metric used by the static route. + default: 1 + state: + description: + - If present, it adds a network static route. + If absent, it removes a network static route. + default: present +notes: + - Works only with Windows 2012 R2 or latest. +author: Daniele Lazzari +''' + +EXAMPLES = r''' +--- + +- name: Add a network static route + win_route: + destination: 192.168.2.10/32 + gateway: 192.168.1.1 + metric: 1 + state: present + +- name: Remove a network static route + win_route: + destination: 192.168.2.10/32 + state: absent +''' +RETURN = r''' +outpot: + description: A message describing the task result. + returned: always + type: string + sample: "Route added" +''' diff --git a/test/integration/targets/win_route/aliases b/test/integration/targets/win_route/aliases new file mode 100644 index 0000000000..90d83328b3 --- /dev/null +++ b/test/integration/targets/win_route/aliases @@ -0,0 +1 @@ +windows/ci/group1 \ No newline at end of file diff --git a/test/integration/targets/win_route/defaults/main.yml b/test/integration/targets/win_route/defaults/main.yml new file mode 100644 index 0000000000..a77ebc16cb --- /dev/null +++ b/test/integration/targets/win_route/defaults/main.yml @@ -0,0 +1,3 @@ +--- +default_gateway: 192.168.1.1 +destination_ip_address: 192.168.2.10 diff --git a/test/integration/targets/win_route/files/Test-Os.ps1 b/test/integration/targets/win_route/files/Test-Os.ps1 new file mode 100644 index 0000000000..f91aa91c40 --- /dev/null +++ b/test/integration/targets/win_route/files/Test-Os.ps1 @@ -0,0 +1,13 @@ +$os = [Environment]::OSVersion +$major = $os.Version.Major +$minor = $os.Version.Minor +$ok = $false + +if(($major -gt 6)){ + $ok = $true +} +elseif (($major -eq 6) -and ($minor -ge 3)){ + $ok = $true +} + +$ok \ No newline at end of file diff --git a/test/integration/targets/win_route/tasks/main.yml b/test/integration/targets/win_route/tasks/main.yml new file mode 100644 index 0000000000..79e1838b9a --- /dev/null +++ b/test/integration/targets/win_route/tasks/main.yml @@ -0,0 +1,29 @@ +--- +# test code for the win_psmodule module when using winrm connection +# (c) 2017, Daniele Lazzari + +# 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 . + + +- name: get os info + script: ../files/Test-Os.ps1 + register: os + +- name: Perform with os Windows 2012R2 or newer + when: os.stdout_lines[0] == "True" + block: + - name: run all tasks + include: tests.yml diff --git a/test/integration/targets/win_route/tasks/tests.yml b/test/integration/targets/win_route/tasks/tests.yml new file mode 100644 index 0000000000..61513aff22 --- /dev/null +++ b/test/integration/targets/win_route/tasks/tests.yml @@ -0,0 +1,74 @@ +--- +- name: add a static route + win_route: + destination: "{{ destination_ip_address }}/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + register: route + +- name: check if route successfully addedd + win_shell: (Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '{{ destination_ip_address }}'").Caption + register: route_added + +- name: test if route successfully addedd + assert: + that: + - route|changed + - route_added.stdout_lines[0] == "{{ destination_ip_address }}" + +- name: add a static route to test idempotency + win_route: + destination: "{{ destination_ip_address }}/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + register: idempotent_route + +- name: test idempotency + assert: + that: + - not idempotent_route|changed + - idempotent_route.output == "Static route already exists" + +- name: remove route + win_route: + destination: "{{ destination_ip_address }}/32" + state: absent + register: route_removed + +- name: check route is removed + win_shell: Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '{{ destination_ip_address }}'" + register: check_route_removed + +- name: test route is removed + assert: + that: + - route_removed|changed + - check_route_removed.stdout == '' + +- name: remove static route to test idempotency + win_route: + destination: "{{ destination_ip_address }}/32" + state: absent + register: idempotent_route_removed + +- name: test idempotency + assert: + that: + - not idempotent_route_removed|changed + - idempotent_route_removed.output == "No route to remove" + +- name: add route to wrong ip address + win_route: + destination: "715.18.0.0/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + ignore_errors: yes + register: wrong_ip + +- name: test route to wrong ip address + assert: + that: + - wrong_ip|failed