Remove incidentals without coverage (#71788)
* Remove incidental_lookup_hashi_vault * Remove incidental_connection_chroot * Remove incidental_selinux * Remove incidental_win_hosts
This commit is contained in:
parent
2d2c5d5ed7
commit
e6e9840717
29 changed files with 0 additions and 2283 deletions
|
@ -1,3 +0,0 @@
|
|||
needs/root
|
||||
shippable/posix/incidental
|
||||
needs/target/connection
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
# Connection tests for POSIX platforms use this script by linking to it from the appropriate 'connection_' target dir.
|
||||
# The name of the inventory group to test is extracted from the directory name following the 'connection_' prefix.
|
||||
|
||||
group=$(python -c \
|
||||
"from os import path; print(path.basename(path.abspath(path.dirname('$0'))).replace('incidental_connection_', ''))")
|
||||
|
||||
cd ../connection
|
||||
|
||||
INVENTORY="../incidental_connection_${group}/test_connection.inventory" ./test.sh \
|
||||
-e target_hosts="${group}" \
|
||||
-e action_prefix= \
|
||||
-e local_tmp=/tmp/ansible-local \
|
||||
-e remote_tmp=/tmp/ansible-remote \
|
||||
"$@"
|
|
@ -1,7 +0,0 @@
|
|||
[chroot]
|
||||
chroot-pipelining ansible_ssh_pipelining=true
|
||||
chroot-no-pipelining ansible_ssh_pipelining=false
|
||||
[chroot:vars]
|
||||
ansible_host=/
|
||||
ansible_connection=chroot
|
||||
ansible_python_interpreter="{{ ansible_playbook_python }}"
|
|
@ -1,7 +0,0 @@
|
|||
shippable/posix/incidental
|
||||
destructive
|
||||
needs/target/incidental_setup_openssl
|
||||
needs/file/test/lib/ansible_test/_data/requirements/constraints.txt
|
||||
skip/aix
|
||||
skip/power/centos
|
||||
skip/python2.6
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
vault_gen_path: 'gen/testproject'
|
||||
vault_kv1_path: 'kv1/testproject'
|
||||
vault_kv2_path: 'kv2/data/testproject'
|
|
@ -1,21 +0,0 @@
|
|||
- name: 'Create an approle policy'
|
||||
shell: "echo '{{ policy }}' | {{ vault_cmd }} policy write approle-policy -"
|
||||
vars:
|
||||
policy: |
|
||||
path "auth/approle/login" {
|
||||
capabilities = [ "create", "read" ]
|
||||
}
|
||||
|
||||
- name: 'Enable the AppRole auth method'
|
||||
command: '{{ vault_cmd }} auth enable approle'
|
||||
|
||||
- name: 'Create a named role'
|
||||
command: '{{ vault_cmd }} write auth/approle/role/test-role policies="test-policy,approle-policy"'
|
||||
|
||||
- name: 'Fetch the RoleID of the AppRole'
|
||||
command: '{{ vault_cmd }} read -field=role_id auth/approle/role/test-role/role-id'
|
||||
register: role_id_cmd
|
||||
|
||||
- name: 'Get a SecretID issued against the AppRole'
|
||||
command: '{{ vault_cmd }} write -field=secret_id -f auth/approle/role/test-role/secret-id'
|
||||
register: secret_id_cmd
|
|
@ -1,45 +0,0 @@
|
|||
- vars:
|
||||
role_id: '{{ role_id_cmd.stdout }}'
|
||||
secret_id: '{{ secret_id_cmd.stdout }}'
|
||||
block:
|
||||
- name: 'Fetch secrets using "hashi_vault" lookup'
|
||||
set_fact:
|
||||
secret1: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret1 auth_method=approle secret_id=' ~ secret_id ~ ' role_id=' ~ role_id) }}"
|
||||
secret2: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret2 auth_method=approle secret_id=' ~ secret_id ~ ' role_id=' ~ role_id) }}"
|
||||
|
||||
- name: 'Check secret values'
|
||||
fail:
|
||||
msg: 'unexpected secret values'
|
||||
when: secret1['value'] != 'foo1' or secret2['value'] != 'foo2'
|
||||
|
||||
- name: 'Failure expected when erroneous credentials are used'
|
||||
vars:
|
||||
secret_wrong_cred: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret2 auth_method=approle secret_id=toto role_id=' ~ role_id) }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_wrong_cred }})'
|
||||
register: test_wrong_cred
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Failure expected when unauthorized secret is read'
|
||||
vars:
|
||||
secret_unauthorized: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret3 auth_method=approle secret_id=' ~ secret_id ~ ' role_id=' ~ role_id) }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_unauthorized }})'
|
||||
register: test_unauthorized
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Failure expected when inexistent secret is read'
|
||||
vars:
|
||||
secret_inexistent: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret4 auth_method=approle secret_id=' ~ secret_id ~ ' role_id=' ~ role_id) }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_inexistent }})'
|
||||
register: test_inexistent
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Check expected failures'
|
||||
assert:
|
||||
msg: "an expected failure didn't occur"
|
||||
that:
|
||||
- test_wrong_cred is failed
|
||||
- test_unauthorized is failed
|
||||
- test_inexistent is failed
|
|
@ -1,155 +0,0 @@
|
|||
---
|
||||
- name: Install Hashi Vault on controlled node and test
|
||||
|
||||
vars:
|
||||
vault_version: '0.11.0'
|
||||
vault_uri: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/lookup_hashi_vault/vault_{{ vault_version }}_{{ ansible_system | lower }}_{{ vault_arch }}.zip'
|
||||
vault_cmd: '{{ local_temp_dir }}/vault'
|
||||
|
||||
block:
|
||||
- name: Create a local temporary directory
|
||||
tempfile:
|
||||
state: directory
|
||||
register: tempfile_result
|
||||
|
||||
- set_fact:
|
||||
local_temp_dir: '{{ tempfile_result.path }}'
|
||||
|
||||
- when: pyopenssl_version.stdout is version('0.15', '>=')
|
||||
block:
|
||||
- name: Generate privatekey
|
||||
openssl_privatekey:
|
||||
path: '{{ local_temp_dir }}/privatekey.pem'
|
||||
|
||||
- name: Generate CSR
|
||||
openssl_csr:
|
||||
path: '{{ local_temp_dir }}/csr.csr'
|
||||
privatekey_path: '{{ local_temp_dir }}/privatekey.pem'
|
||||
subject:
|
||||
commonName: localhost
|
||||
|
||||
- name: Generate selfsigned certificate
|
||||
openssl_certificate:
|
||||
path: '{{ local_temp_dir }}/cert.pem'
|
||||
csr_path: '{{ local_temp_dir }}/csr.csr'
|
||||
privatekey_path: '{{ local_temp_dir }}/privatekey.pem'
|
||||
provider: selfsigned
|
||||
selfsigned_digest: sha256
|
||||
register: selfsigned_certificate
|
||||
|
||||
- name: 'Install unzip'
|
||||
package:
|
||||
name: unzip
|
||||
when: ansible_distribution != "MacOSX" # unzip already installed
|
||||
|
||||
- assert:
|
||||
# Linux: x86_64, FreeBSD: amd64
|
||||
that: ansible_architecture in ['i386', 'x86_64', 'amd64']
|
||||
- set_fact:
|
||||
vault_arch: '386'
|
||||
when: ansible_architecture == 'i386'
|
||||
- set_fact:
|
||||
vault_arch: amd64
|
||||
when: ansible_architecture in ['x86_64', 'amd64']
|
||||
|
||||
- name: 'Download vault binary'
|
||||
unarchive:
|
||||
src: '{{ vault_uri }}'
|
||||
dest: '{{ local_temp_dir }}'
|
||||
remote_src: true
|
||||
|
||||
- environment:
|
||||
# used by vault command
|
||||
VAULT_DEV_ROOT_TOKEN_ID: '47542cbc-6bf8-4fba-8eda-02e0a0d29a0a'
|
||||
block:
|
||||
- name: 'Create configuration file'
|
||||
template:
|
||||
src: vault_config.hcl.j2
|
||||
dest: '{{ local_temp_dir }}/vault_config.hcl'
|
||||
|
||||
- name: 'Start vault service'
|
||||
environment:
|
||||
VAULT_ADDR: 'http://localhost:8200'
|
||||
block:
|
||||
- name: 'Start vault server (dev mode enabled)'
|
||||
shell: 'nohup {{ vault_cmd }} server -dev -config {{ local_temp_dir }}/vault_config.hcl </dev/null >/dev/null 2>&1 &'
|
||||
|
||||
- name: 'Create generic secrets engine'
|
||||
command: '{{ vault_cmd }} secrets enable -path=gen generic'
|
||||
|
||||
- name: 'Create KV v1 secrets engine'
|
||||
command: '{{ vault_cmd }} secrets enable -path=kv1 -version=1 kv'
|
||||
|
||||
- name: 'Create KV v2 secrets engine'
|
||||
command: '{{ vault_cmd }} secrets enable -path=kv2 -version=2 kv'
|
||||
|
||||
- name: 'Create a test policy'
|
||||
shell: "echo '{{ policy }}' | {{ vault_cmd }} policy write test-policy -"
|
||||
vars:
|
||||
policy: |
|
||||
path "{{ vault_gen_path }}/secret1" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "{{ vault_gen_path }}/secret2" {
|
||||
capabilities = ["read", "update"]
|
||||
}
|
||||
path "{{ vault_gen_path }}/secret3" {
|
||||
capabilities = ["deny"]
|
||||
}
|
||||
path "{{ vault_kv1_path }}/secret1" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "{{ vault_kv1_path }}/secret2" {
|
||||
capabilities = ["read", "update"]
|
||||
}
|
||||
path "{{ vault_kv1_path }}/secret3" {
|
||||
capabilities = ["deny"]
|
||||
}
|
||||
path "{{ vault_kv2_path }}/secret1" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "{{ vault_kv2_path }}/secret2" {
|
||||
capabilities = ["read", "update"]
|
||||
}
|
||||
path "{{ vault_kv2_path }}/secret3" {
|
||||
capabilities = ["deny"]
|
||||
}
|
||||
|
||||
- name: 'Create generic secrets'
|
||||
command: '{{ vault_cmd }} write {{ vault_gen_path }}/secret{{ item }} value=foo{{ item }}'
|
||||
loop: [1, 2, 3]
|
||||
|
||||
- name: 'Create KV v1 secrets'
|
||||
command: '{{ vault_cmd }} kv put {{ vault_kv1_path }}/secret{{ item }} value=foo{{ item }}'
|
||||
loop: [1, 2, 3]
|
||||
|
||||
- name: 'Create KV v2 secrets'
|
||||
command: '{{ vault_cmd }} kv put {{ vault_kv2_path | regex_replace("/data") }}/secret{{ item }} value=foo{{ item }}'
|
||||
loop: [1, 2, 3]
|
||||
|
||||
- name: setup approle auth
|
||||
import_tasks: approle_setup.yml
|
||||
when: ansible_distribution != 'RedHat' or ansible_distribution_major_version is version('7', '>')
|
||||
|
||||
- name: setup token auth
|
||||
import_tasks: token_setup.yml
|
||||
|
||||
- import_tasks: tests.yml
|
||||
vars:
|
||||
auth_type: approle
|
||||
when: ansible_distribution != 'RedHat' or ansible_distribution_major_version is version('7', '>')
|
||||
|
||||
- import_tasks: tests.yml
|
||||
vars:
|
||||
auth_type: token
|
||||
|
||||
always:
|
||||
- name: 'Kill vault process'
|
||||
shell: "kill $(cat {{ local_temp_dir }}/vault.pid)"
|
||||
ignore_errors: true
|
||||
|
||||
always:
|
||||
- name: 'Delete temp dir'
|
||||
file:
|
||||
path: '{{ local_temp_dir }}'
|
||||
state: absent
|
|
@ -1,35 +0,0 @@
|
|||
- name: 'test {{ auth_type }} auth without SSL (lookup parameters)'
|
||||
include_tasks: '{{ auth_type }}_test.yml'
|
||||
vars:
|
||||
conn_params: 'url=http://localhost:8200 '
|
||||
|
||||
- name: 'test {{ auth_type }} auth without SSL (environment variable)'
|
||||
include_tasks: '{{ auth_type }}_test.yml'
|
||||
args:
|
||||
apply:
|
||||
vars:
|
||||
conn_params: ''
|
||||
environment:
|
||||
VAULT_ADDR: 'http://localhost:8200'
|
||||
|
||||
- when: pyopenssl_version.stdout is version('0.15', '>=')
|
||||
block:
|
||||
- name: 'test {{ auth_type }} auth with certs (validation enabled, lookup parameters)'
|
||||
include_tasks: '{{ auth_type }}_test.yml'
|
||||
vars:
|
||||
conn_params: 'url=https://localhost:8201 ca_cert={{ local_temp_dir }}/cert.pem validate_certs=True '
|
||||
|
||||
- name: 'test {{ auth_type }} auth with certs (validation enabled, environment variables)'
|
||||
include_tasks: '{{ auth_type }}_test.yml'
|
||||
args:
|
||||
apply:
|
||||
vars:
|
||||
conn_params: ''
|
||||
environment:
|
||||
VAULT_ADDR: 'https://localhost:8201'
|
||||
VAULT_CACERT: '{{ local_temp_dir }}/cert.pem'
|
||||
|
||||
- name: 'test {{ auth_type }} auth with certs (validation disabled, lookup parameters)'
|
||||
include_tasks: '{{ auth_type }}_test.yml'
|
||||
vars:
|
||||
conn_params: 'url=https://localhost:8201 validate_certs=False '
|
|
@ -1,3 +0,0 @@
|
|||
- name: 'Create a test credentials (token)'
|
||||
command: '{{ vault_cmd }} token create -policy test-policy -field token'
|
||||
register: user_token_cmd
|
|
@ -1,58 +0,0 @@
|
|||
- vars:
|
||||
user_token: '{{ user_token_cmd.stdout }}'
|
||||
block:
|
||||
- name: 'Fetch secrets using "hashi_vault" lookup'
|
||||
set_fact:
|
||||
gen_secret1: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_gen_path ~ '/secret1 auth_method=token token=' ~ user_token) }}"
|
||||
gen_secret2: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_gen_path ~ '/secret2 token=' ~ user_token) }}"
|
||||
kv1_secret1: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv1_path ~ '/secret1 auth_method=token token=' ~ user_token) }}"
|
||||
kv1_secret2: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv1_path ~ '/secret2 token=' ~ user_token) }}"
|
||||
kv2_secret1: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret1 auth_method=token token=' ~ user_token) }}"
|
||||
kv2_secret2: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret2 token=' ~ user_token) }}"
|
||||
|
||||
- name: 'Check secret generic values'
|
||||
fail:
|
||||
msg: 'unexpected secret values'
|
||||
when: gen_secret1['value'] != 'foo1' or gen_secret2['value'] != 'foo2'
|
||||
|
||||
- name: 'Check secret kv1 values'
|
||||
fail:
|
||||
msg: 'unexpected secret values'
|
||||
when: kv1_secret1['value'] != 'foo1' or kv1_secret2['value'] != 'foo2'
|
||||
|
||||
- name: 'Check secret kv2 values'
|
||||
fail:
|
||||
msg: 'unexpected secret values'
|
||||
when: kv2_secret1['value'] != 'foo1' or kv2_secret2['value'] != 'foo2'
|
||||
|
||||
- name: 'Failure expected when erroneous credentials are used'
|
||||
vars:
|
||||
secret_wrong_cred: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret2 auth_method=token token=wrong_token') }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_wrong_cred }})'
|
||||
register: test_wrong_cred
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Failure expected when unauthorized secret is read'
|
||||
vars:
|
||||
secret_unauthorized: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret3 token=' ~ user_token) }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_unauthorized }})'
|
||||
register: test_unauthorized
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Failure expected when inexistent secret is read'
|
||||
vars:
|
||||
secret_inexistent: "{{ lookup('hashi_vault', conn_params ~ 'secret=' ~ vault_kv2_path ~ '/secret4 token=' ~ user_token) }}"
|
||||
debug:
|
||||
msg: 'Failure is expected ({{ secret_inexistent }})'
|
||||
register: test_inexistent
|
||||
ignore_errors: true
|
||||
|
||||
- name: 'Check expected failures'
|
||||
assert:
|
||||
msg: "an expected failure didn't occur"
|
||||
that:
|
||||
- test_wrong_cred is failed
|
||||
- test_unauthorized is failed
|
||||
- test_inexistent is failed
|
|
@ -1,10 +0,0 @@
|
|||
# {{ ansible_managed }}
|
||||
pid_file = "{{ local_temp_dir }}/vault.pid"
|
||||
{% if pyopenssl_version.stdout is version('0.15', '>=') %}
|
||||
listener "tcp" {
|
||||
tls_key_file = "{{ local_temp_dir }}/privatekey.pem"
|
||||
tls_cert_file = "{{ local_temp_dir }}/cert.pem"
|
||||
tls_disable = false
|
||||
address = "localhost:8201"
|
||||
}
|
||||
{% endif %}
|
|
@ -1,19 +0,0 @@
|
|||
- hosts: localhost
|
||||
tasks:
|
||||
- name: Install openssl
|
||||
import_role:
|
||||
name: incidental_setup_openssl
|
||||
|
||||
- name: "RedHat <= 7, select last version compatible with request 2.6.0 (this version doesn't support approle auth)"
|
||||
set_fact:
|
||||
hvac_package: 'hvac==0.2.5'
|
||||
when: ansible_distribution == 'RedHat' and ansible_distribution_major_version is version('7', '<=')
|
||||
|
||||
- name: 'CentOS < 7, select last version compatible with Python 2.6'
|
||||
set_fact:
|
||||
hvac_package: 'hvac==0.5.0'
|
||||
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version is version('7', '<')
|
||||
|
||||
- name: 'Install hvac Python package'
|
||||
pip:
|
||||
name: "{{ hvac_package|default('hvac') }}"
|
|
@ -1,9 +0,0 @@
|
|||
- hosts: localhost
|
||||
tasks:
|
||||
- name: register pyOpenSSL version
|
||||
command: "{{ ansible_python.executable }} -c 'import OpenSSL; print(OpenSSL.__version__)'"
|
||||
register: pyopenssl_version
|
||||
|
||||
- name: Test lookup hashi_vault
|
||||
import_role:
|
||||
name: incidental_lookup_hashi_vault/lookup_hashi_vault
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
# First install pyOpenSSL, then test lookup in a second playbook in order to
|
||||
# workaround this error which occurs on OS X 10.11 only:
|
||||
#
|
||||
# TASK [lookup_hashi_vault : test token auth with certs (validation enabled, lookup parameters)] ***
|
||||
# included: lookup_hashi_vault/tasks/token_test.yml for testhost
|
||||
#
|
||||
# TASK [lookup_hashi_vault : Fetch secrets using "hashi_vault" lookup] ***
|
||||
# From cffi callback <function _verify_callback at 0x106f995f0>:
|
||||
# Traceback (most recent call last):
|
||||
# File "/usr/local/lib/python2.7/site-packages/OpenSSL/SSL.py", line 309, in wrapper
|
||||
# _lib.X509_up_ref(x509)
|
||||
# AttributeError: 'module' object has no attribute 'X509_up_ref'
|
||||
# fatal: [testhost]: FAILED! => { "msg": "An unhandled exception occurred while running the lookup plugin 'hashi_vault'. Error was a <class 'requests.exceptions.SSLError'>, original message: HTTPSConnectionPool(host='localhost', port=8201): Max retries exceeded with url: /v1/auth/token/lookup-self (Caused by SSLError(SSLError(\"bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)\",),))"}
|
||||
|
||||
ANSIBLE_ROLES_PATH=../ \
|
||||
ansible-playbook playbooks/install_dependencies.yml -v "$@"
|
||||
|
||||
ANSIBLE_ROLES_PATH=../ \
|
||||
ansible-playbook playbooks/test_lookup_hashi_vault.yml -v "$@"
|
|
@ -1,3 +0,0 @@
|
|||
needs/root
|
||||
shippable/posix/incidental
|
||||
skip/aix
|
|
@ -1,36 +0,0 @@
|
|||
# (c) 2017, Sam Doran <sdoran@redhat.com>
|
||||
|
||||
# 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/>.
|
||||
|
||||
- debug:
|
||||
msg: SELinux is disabled
|
||||
when: ansible_selinux is defined and ansible_selinux == False
|
||||
|
||||
- debug:
|
||||
msg: SELinux is {{ ansible_selinux.status }}
|
||||
when: ansible_selinux is defined and ansible_selinux != False
|
||||
|
||||
- include: selinux.yml
|
||||
when:
|
||||
- ansible_selinux is defined
|
||||
- ansible_selinux != False
|
||||
- ansible_selinux.status == 'enabled'
|
||||
|
||||
- include: selogin.yml
|
||||
when:
|
||||
- ansible_selinux is defined
|
||||
- ansible_selinux != False
|
||||
- ansible_selinux.status == 'enabled'
|
|
@ -1,364 +0,0 @@
|
|||
# (c) 2017, Sam Doran <sdoran@redhat.com>
|
||||
|
||||
# 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/>.
|
||||
|
||||
|
||||
# First Test
|
||||
# ##############################################################################
|
||||
# Test changing the state, which requires a reboot
|
||||
|
||||
- name: TEST 1 | Get current SELinux config file contents
|
||||
set_fact:
|
||||
selinux_config_original: "{{ lookup('file', '/etc/sysconfig/selinux').split('\n') }}"
|
||||
before_test_sestatus: "{{ ansible_selinux }}"
|
||||
|
||||
- debug:
|
||||
var: "{{ item }}"
|
||||
verbosity: 1
|
||||
with_items:
|
||||
- selinux_config_original
|
||||
- before_test_sestatus
|
||||
- ansible_selinux
|
||||
|
||||
- name: TEST 1 | Setup SELinux configuration for tests
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
|
||||
- name: TEST 1 | Disable SELinux
|
||||
selinux:
|
||||
state: disabled
|
||||
policy: targeted
|
||||
register: _disable_test1
|
||||
|
||||
- debug:
|
||||
var: _disable_test1
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 1 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- name: TEST 1 | Assert that status was changed, reboot_required is True, a warning was displayed, and SELinux is configured properly
|
||||
assert:
|
||||
that:
|
||||
- _disable_test1 is changed
|
||||
- _disable_test1.reboot_required
|
||||
- (_disable_test1.warnings | length ) >= 1
|
||||
- ansible_selinux.config_mode == 'disabled'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 1 | Disable SELinux again
|
||||
selinux:
|
||||
state: disabled
|
||||
policy: targeted
|
||||
register: _disable_test2
|
||||
|
||||
- debug:
|
||||
var: _disable_test2
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 1 | Assert that no change is reported, a warnking was dispalyed, and reboot_required is True
|
||||
assert:
|
||||
that:
|
||||
- _disable_test2 is not changed
|
||||
- (_disable_test1.warnings | length ) >= 1
|
||||
- _disable_test2.reboot_required
|
||||
|
||||
- name: TEST 1 | Get modified config file
|
||||
set_fact:
|
||||
selinux_config_after: "{{ lookup('file', '/etc/sysconfig/selinux').split('\n') }}"
|
||||
|
||||
- debug:
|
||||
var: selinux_config_after
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 1 | Ensure SELinux config file is properly formatted
|
||||
assert:
|
||||
that:
|
||||
- selinux_config_original | length == selinux_config_after | length
|
||||
- selinux_config_after[selinux_config_after.index('SELINUX=disabled')] is search("^SELINUX=\w+$")
|
||||
- selinux_config_after[selinux_config_after.index('SELINUXTYPE=targeted')] is search("^SELINUXTYPE=\w+$")
|
||||
|
||||
- name: TEST 1 | Reset SELinux configuration for next test
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
|
||||
|
||||
# Second Test
|
||||
# ##############################################################################
|
||||
# Test changing only the policy, which does not require a reboot
|
||||
|
||||
- name: TEST 2 | Make sure the policy is present
|
||||
package:
|
||||
name: selinux-policy-mls
|
||||
state: present
|
||||
|
||||
- name: TEST 2 | Set SELinux policy
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: mls
|
||||
register: _state_test1
|
||||
|
||||
- debug:
|
||||
var: _state_test1
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 2 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
tags: debug
|
||||
|
||||
- name: TEST 2 | Assert that status was changed, reboot_required is False, no warnings were displayed, and SELinux is configured properly
|
||||
assert:
|
||||
that:
|
||||
- _state_test1 is changed
|
||||
- not _state_test1.reboot_required
|
||||
- _state_test1.warnings is not defined
|
||||
- ansible_selinux.config_mode == 'enforcing'
|
||||
- ansible_selinux.type == 'mls'
|
||||
|
||||
- name: TEST 2 | Set SELinux policy again
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: mls
|
||||
register: _state_test2
|
||||
|
||||
- debug:
|
||||
var: _state_test2
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 2 | Assert that no change was reported, no warnings were dispalyed, and reboot_required is False
|
||||
assert:
|
||||
that:
|
||||
- _state_test2 is not changed
|
||||
- _state_test2.warnings is not defined
|
||||
- not _state_test2.reboot_required
|
||||
|
||||
- name: TEST 2 | Get modified config file
|
||||
set_fact:
|
||||
selinux_config_after: "{{ lookup('file', '/etc/sysconfig/selinux').split('\n') }}"
|
||||
|
||||
- debug:
|
||||
var: selinux_config_after
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 2 | Ensure SELinux config file is properly formatted
|
||||
assert:
|
||||
that:
|
||||
- selinux_config_original | length == selinux_config_after | length
|
||||
- selinux_config_after[selinux_config_after.index('SELINUX=enforcing')] is search("^SELINUX=\w+$")
|
||||
- selinux_config_after[selinux_config_after.index('SELINUXTYPE=mls')] is search("^SELINUXTYPE=\w+$")
|
||||
|
||||
- name: TEST 2 | Reset SELinux configuration for next test
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
|
||||
|
||||
# Third Test
|
||||
# ##############################################################################
|
||||
# Test changing non-existing policy
|
||||
|
||||
- name: TEST 3 | Set SELinux policy
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: non-existing-selinux-policy
|
||||
register: _state_test1
|
||||
ignore_errors: yes
|
||||
|
||||
- debug:
|
||||
var: _state_test1
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 3 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
tags: debug
|
||||
|
||||
- name: TEST 3 | Assert that status was not changed, the task failed, the msg contains proper information and SELinux was not changed
|
||||
assert:
|
||||
that:
|
||||
- _state_test1 is not changed
|
||||
- _state_test1 is failed
|
||||
- _state_test1.msg == 'Policy non-existing-selinux-policy does not exist in /etc/selinux/'
|
||||
- ansible_selinux.config_mode == 'enforcing'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
|
||||
# Fourth Test
|
||||
# ##############################################################################
|
||||
# Test if check mode returns correct changed values and
|
||||
# doesn't make any changes
|
||||
|
||||
|
||||
- name: TEST 4 | Set SELinux to enforcing
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
register: _check_mode_test1
|
||||
|
||||
- debug:
|
||||
var: _check_mode_test1
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 4 | Set SELinux to enforcing in check mode
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
register: _check_mode_test1
|
||||
check_mode: yes
|
||||
|
||||
- name: TEST 4 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
tags: debug
|
||||
|
||||
- name: TEST 4 | Assert that check mode is idempotent
|
||||
assert:
|
||||
that:
|
||||
- _check_mode_test1 is success
|
||||
- not _check_mode_test1.reboot_required
|
||||
- ansible_selinux.config_mode == 'enforcing'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
- name: TEST 4 | Set SELinux to permissive in check mode
|
||||
selinux:
|
||||
state: permissive
|
||||
policy: targeted
|
||||
register: _check_mode_test2
|
||||
check_mode: yes
|
||||
|
||||
- name: TEST 4 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
tags: debug
|
||||
|
||||
- name: TEST 4 | Assert that check mode doesn't set state permissive and returns changed
|
||||
assert:
|
||||
that:
|
||||
- _check_mode_test2 is changed
|
||||
- not _check_mode_test2.reboot_required
|
||||
- ansible_selinux.config_mode == 'enforcing'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
- name: TEST 4 | Disable SELinux in check mode
|
||||
selinux:
|
||||
state: disabled
|
||||
register: _check_mode_test3
|
||||
check_mode: yes
|
||||
|
||||
- name: TEST 4 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
tags: debug
|
||||
|
||||
- name: TEST 4 | Assert that check mode didn't change anything, status is changed, reboot_required is True, a warning was displayed
|
||||
assert:
|
||||
that:
|
||||
- _check_mode_test3 is changed
|
||||
- _check_mode_test3.reboot_required
|
||||
- (_check_mode_test3.warnings | length ) >= 1
|
||||
- ansible_selinux.config_mode == 'enforcing'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
- name: TEST 4 | Set SELinux to permissive
|
||||
selinux:
|
||||
state: permissive
|
||||
policy: targeted
|
||||
register: _check_mode_test4
|
||||
|
||||
- debug:
|
||||
var: _check_mode_test4
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 4 | Disable SELinux in check mode
|
||||
selinux:
|
||||
state: disabled
|
||||
register: _check_mode_test4
|
||||
check_mode: yes
|
||||
|
||||
- name: TEST 4 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
tags: debug
|
||||
|
||||
- name: TEST 4 | Assert that check mode didn't change anything, status is changed, reboot_required is True, a warning was displayed
|
||||
assert:
|
||||
that:
|
||||
- _check_mode_test4 is changed
|
||||
- _check_mode_test4.reboot_required
|
||||
- (_check_mode_test3.warnings | length ) >= 1
|
||||
- ansible_selinux.config_mode == 'permissive'
|
||||
- ansible_selinux.type == 'targeted'
|
||||
|
||||
- name: TEST 4 | Set SELinux to enforcing
|
||||
selinux:
|
||||
state: enforcing
|
||||
policy: targeted
|
||||
register: _check_mode_test5
|
||||
|
||||
- debug:
|
||||
var: _check_mode_test5
|
||||
verbosity: 1
|
||||
|
||||
- name: TEST 4 | Disable SELinux
|
||||
selinux:
|
||||
state: disabled
|
||||
register: _check_mode_test5
|
||||
|
||||
- name: TEST 4 | Disable SELinux in check mode
|
||||
selinux:
|
||||
state: disabled
|
||||
register: _check_mode_test5
|
||||
check_mode: yes
|
||||
|
||||
- name: TEST 4 | Re-gather facts
|
||||
setup:
|
||||
|
||||
- debug:
|
||||
var: ansible_selinux
|
||||
verbosity: 1
|
||||
tags: debug
|
||||
|
||||
- name: TEST 4 | Assert that in check mode status was not changed, reboot_required is True, a warning was displayed, and SELinux is configured properly
|
||||
assert:
|
||||
that:
|
||||
- _check_mode_test5 is success
|
||||
- _check_mode_test5.reboot_required
|
||||
- (_check_mode_test5.warnings | length ) >= 1
|
||||
- ansible_selinux.config_mode == 'disabled'
|
||||
- ansible_selinux.type == 'targeted'
|
|
@ -1,81 +0,0 @@
|
|||
---
|
||||
|
||||
- name: create user for testing
|
||||
user:
|
||||
name: seuser
|
||||
|
||||
- name: attempt to add mapping without 'seuser'
|
||||
selogin:
|
||||
login: seuser
|
||||
register: selogin_error
|
||||
ignore_errors: yes
|
||||
|
||||
- name: verify failure
|
||||
assert:
|
||||
that:
|
||||
- selogin_error is failed
|
||||
|
||||
- name: map login to SELinux user
|
||||
selogin:
|
||||
login: seuser
|
||||
seuser: staff_u
|
||||
register: selogin_new_mapping
|
||||
check_mode: "{{ item }}"
|
||||
with_items:
|
||||
- yes
|
||||
- no
|
||||
- yes
|
||||
- no
|
||||
|
||||
- name: new mapping- verify functionality and check_mode
|
||||
assert:
|
||||
that:
|
||||
- selogin_new_mapping.results[0] is changed
|
||||
- selogin_new_mapping.results[1] is changed
|
||||
- selogin_new_mapping.results[2] is not changed
|
||||
- selogin_new_mapping.results[3] is not changed
|
||||
|
||||
- name: change SELinux user login mapping
|
||||
selogin:
|
||||
login: seuser
|
||||
seuser: user_u
|
||||
register: selogin_mod_mapping
|
||||
check_mode: "{{ item }}"
|
||||
with_items:
|
||||
- yes
|
||||
- no
|
||||
- yes
|
||||
- no
|
||||
|
||||
- name: changed mapping- verify functionality and check_mode
|
||||
assert:
|
||||
that:
|
||||
- selogin_mod_mapping.results[0] is changed
|
||||
- selogin_mod_mapping.results[1] is changed
|
||||
- selogin_mod_mapping.results[2] is not changed
|
||||
- selogin_mod_mapping.results[3] is not changed
|
||||
|
||||
- name: remove SELinux user mapping
|
||||
selogin:
|
||||
login: seuser
|
||||
state: absent
|
||||
register: selogin_del_mapping
|
||||
check_mode: "{{ item }}"
|
||||
with_items:
|
||||
- yes
|
||||
- no
|
||||
- yes
|
||||
- no
|
||||
|
||||
- name: delete mapping- verify functionality and check_mode
|
||||
assert:
|
||||
that:
|
||||
- selogin_del_mapping.results[0] is changed
|
||||
- selogin_del_mapping.results[1] is changed
|
||||
- selogin_del_mapping.results[2] is not changed
|
||||
- selogin_del_mapping.results[3] is not changed
|
||||
|
||||
- name: remove test user
|
||||
user:
|
||||
name: seuser
|
||||
state: absent
|
|
@ -1,2 +0,0 @@
|
|||
shippable/windows/incidental
|
||||
windows
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
test_win_hosts_cname: testhost
|
||||
test_win_hosts_ip: 192.168.168.1
|
||||
|
||||
test_win_hosts_aliases_set:
|
||||
- alias1
|
||||
- alias2
|
||||
- alias3
|
||||
- alias4
|
||||
|
||||
test_win_hosts_aliases_remove:
|
||||
- alias3
|
||||
- alias4
|
|
@ -1,2 +0,0 @@
|
|||
dependencies:
|
||||
- setup_remote_tmp_dir
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
- name: take a copy of the original hosts file
|
||||
win_copy:
|
||||
src: C:\Windows\System32\drivers\etc\hosts
|
||||
dest: '{{ remote_tmp_dir }}\hosts'
|
||||
remote_src: yes
|
||||
|
||||
- block:
|
||||
- name: run tests
|
||||
include_tasks: tests.yml
|
||||
|
||||
always:
|
||||
- name: restore hosts file
|
||||
win_copy:
|
||||
src: '{{ remote_tmp_dir }}\hosts'
|
||||
dest: C:\Windows\System32\drivers\etc\hosts
|
||||
remote_src: yes
|
|
@ -1,189 +0,0 @@
|
|||
---
|
||||
|
||||
- name: add a simple host with address
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
register: add_ip
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "add_ip.changed == true"
|
||||
|
||||
- name: get actual dns result
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: add_ip_actual
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "add_ip_actual.stdout_lines[0]|lower == 'true'"
|
||||
|
||||
- name: add a simple host with ipv4 address (idempotent)
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
register: add_ip
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "add_ip.changed == false"
|
||||
|
||||
- name: remove simple host
|
||||
win_hosts:
|
||||
state: absent
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
register: remove_ip
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "remove_ip.changed == true"
|
||||
|
||||
- name: get actual dns result
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname}}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: remove_ip_actual
|
||||
failed_when: "remove_ip_actual.rc == 0"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "remove_ip_actual.stdout_lines[0]|lower == 'false'"
|
||||
|
||||
- name: remove simple host (idempotent)
|
||||
win_hosts:
|
||||
state: absent
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
register: remove_ip
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "remove_ip.changed == false"
|
||||
|
||||
- name: add host and set aliases
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}"
|
||||
action: set
|
||||
register: set_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "set_aliases.changed == true"
|
||||
|
||||
- name: get actual dns result for host
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: set_aliases_actual_host
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "set_aliases_actual_host.stdout_lines[0]|lower == 'true'"
|
||||
|
||||
- name: get actual dns results for aliases
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: set_aliases_actual
|
||||
with_items: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "item.stdout_lines[0]|lower == 'true'"
|
||||
with_items: "{{ set_aliases_actual.results }}"
|
||||
|
||||
- name: add host and set aliases (idempotent)
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}"
|
||||
action: set
|
||||
register: set_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "set_aliases.changed == false"
|
||||
|
||||
- name: remove aliases from the list
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_remove }}"
|
||||
action: remove
|
||||
register: remove_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "remove_aliases.changed == true"
|
||||
|
||||
- name: get actual dns result for removed aliases
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: remove_aliases_removed_actual
|
||||
failed_when: "remove_aliases_removed_actual.rc == 0"
|
||||
with_items: "{{ test_win_hosts_aliases_remove }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "item.stdout_lines[0]|lower == 'false'"
|
||||
with_items: "{{ remove_aliases_removed_actual.results }}"
|
||||
|
||||
- name: get actual dns result for remaining aliases
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: remove_aliases_remain_actual
|
||||
with_items: "{{ test_win_hosts_aliases_set | difference(test_win_hosts_aliases_remove) }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "item.stdout_lines[0]|lower == 'true'"
|
||||
with_items: "{{ remove_aliases_remain_actual.results }}"
|
||||
|
||||
- name: remove aliases from the list (idempotent)
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_remove }}"
|
||||
action: remove
|
||||
register: remove_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "remove_aliases.changed == false"
|
||||
|
||||
- name: add aliases back
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_remove }}"
|
||||
action: add
|
||||
register: add_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "add_aliases.changed == true"
|
||||
|
||||
- name: get actual dns results for aliases
|
||||
win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }"
|
||||
register: add_aliases_actual
|
||||
with_items: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "item.stdout_lines[0]|lower == 'true'"
|
||||
with_items: "{{ add_aliases_actual.results }}"
|
||||
|
||||
- name: add aliases back (idempotent)
|
||||
win_hosts:
|
||||
state: present
|
||||
ip_address: "{{ test_win_hosts_ip }}"
|
||||
canonical_name: "{{ test_win_hosts_cname }}"
|
||||
aliases: "{{ test_win_hosts_aliases_remove }}"
|
||||
action: add
|
||||
register: add_aliases
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "add_aliases.changed == false"
|
|
@ -1,208 +0,0 @@
|
|||
# Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# (c) 2013, Maykel Moya <mmoya@speedyrails.com>
|
||||
# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
author: Maykel Moya <mmoya@speedyrails.com>
|
||||
connection: chroot
|
||||
short_description: Interact with local chroot
|
||||
description:
|
||||
- Run commands or put/fetch files to an existing chroot on the Ansible controller.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
remote_addr:
|
||||
description:
|
||||
- The path of the chroot you want to access.
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: ansible_host
|
||||
executable:
|
||||
description:
|
||||
- User specified executable shell
|
||||
ini:
|
||||
- section: defaults
|
||||
key: executable
|
||||
env:
|
||||
- name: ANSIBLE_EXECUTABLE
|
||||
vars:
|
||||
- name: ansible_executable
|
||||
default: /bin/sh
|
||||
chroot_exe:
|
||||
version_added: '2.8'
|
||||
description:
|
||||
- User specified chroot binary
|
||||
ini:
|
||||
- section: chroot_connection
|
||||
key: exe
|
||||
env:
|
||||
- name: ANSIBLE_CHROOT_EXE
|
||||
vars:
|
||||
- name: ansible_chroot_exe
|
||||
default: chroot
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.basic import is_executable
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_bytes, to_native
|
||||
from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
class Connection(ConnectionBase):
|
||||
''' Local chroot based connections '''
|
||||
|
||||
transport = 'chroot'
|
||||
has_pipelining = True
|
||||
# su currently has an undiagnosed issue with calculating the file
|
||||
# checksums (so copy, for instance, doesn't work right)
|
||||
# Have to look into that before re-enabling this
|
||||
has_tty = False
|
||||
|
||||
default_user = 'root'
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
||||
self.chroot = self._play_context.remote_addr
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise AnsibleError("chroot connection requires running as root")
|
||||
|
||||
# we're running as root on the local system so do some
|
||||
# trivial checks for ensuring 'host' is actually a chroot'able dir
|
||||
if not os.path.isdir(self.chroot):
|
||||
raise AnsibleError("%s is not a directory" % self.chroot)
|
||||
|
||||
chrootsh = os.path.join(self.chroot, 'bin/sh')
|
||||
# Want to check for a usable bourne shell inside the chroot.
|
||||
# is_executable() == True is sufficient. For symlinks it
|
||||
# gets really complicated really fast. So we punt on finding that
|
||||
# out. As long as it's a symlink we assume that it will work
|
||||
if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):
|
||||
raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot)
|
||||
|
||||
def _connect(self):
|
||||
''' connect to the chroot '''
|
||||
if os.path.isabs(self.get_option('chroot_exe')):
|
||||
self.chroot_cmd = self.get_option('chroot_exe')
|
||||
else:
|
||||
try:
|
||||
self.chroot_cmd = get_bin_path(self.get_option('chroot_exe'))
|
||||
except ValueError as e:
|
||||
raise AnsibleError(to_native(e))
|
||||
|
||||
super(Connection, self)._connect()
|
||||
if not self._connected:
|
||||
display.vvv("THIS IS A LOCAL CHROOT DIR", host=self.chroot)
|
||||
self._connected = True
|
||||
|
||||
def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE):
|
||||
''' run a command on the chroot. This is only needed for implementing
|
||||
put_file() get_file() so that we don't have to read the whole file
|
||||
into memory.
|
||||
|
||||
compared to exec_command() it looses some niceties like being able to
|
||||
return the process's exit code immediately.
|
||||
'''
|
||||
executable = self.get_option('executable')
|
||||
local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
|
||||
|
||||
display.vvv("EXEC %s" % (local_cmd), host=self.chroot)
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
return p
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=False):
|
||||
''' run a command on the chroot '''
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
p = self._buffered_exec_command(cmd)
|
||||
|
||||
stdout, stderr = p.communicate(in_data)
|
||||
return (p.returncode, stdout, stderr)
|
||||
|
||||
def _prefix_login_path(self, remote_path):
|
||||
''' Make sure that we put files into a standard path
|
||||
|
||||
If a path is relative, then we need to choose where to put it.
|
||||
ssh chooses $HOME but we aren't guaranteed that a home dir will
|
||||
exist in any given chroot. So for now we're choosing "/" instead.
|
||||
This also happens to be the former default.
|
||||
|
||||
Can revisit using $HOME instead if it's a problem
|
||||
'''
|
||||
if not remote_path.startswith(os.path.sep):
|
||||
remote_path = os.path.join(os.path.sep, remote_path)
|
||||
return os.path.normpath(remote_path)
|
||||
|
||||
def put_file(self, in_path, out_path):
|
||||
''' transfer a file from local to chroot '''
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.chroot)
|
||||
|
||||
out_path = shlex_quote(self._prefix_login_path(out_path))
|
||||
try:
|
||||
with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file:
|
||||
if not os.fstat(in_file.fileno()).st_size:
|
||||
count = ' count=0'
|
||||
else:
|
||||
count = ''
|
||||
try:
|
||||
p = self._buffered_exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), stdin=in_file)
|
||||
except OSError:
|
||||
raise AnsibleError("chroot connection requires dd command in the chroot")
|
||||
try:
|
||||
stdout, stderr = p.communicate()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
|
||||
except IOError:
|
||||
raise AnsibleError("file or module does not exist at: %s" % in_path)
|
||||
|
||||
def fetch_file(self, in_path, out_path):
|
||||
''' fetch a file from chroot to local '''
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.chroot)
|
||||
|
||||
in_path = shlex_quote(self._prefix_login_path(in_path))
|
||||
try:
|
||||
p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
|
||||
except OSError:
|
||||
raise AnsibleError("chroot connection requires dd command in the chroot")
|
||||
|
||||
with open(to_bytes(out_path, errors='surrogate_or_strict'), 'wb+') as out_file:
|
||||
try:
|
||||
chunk = p.stdout.read(BUFSIZE)
|
||||
while chunk:
|
||||
out_file.write(chunk)
|
||||
chunk = p.stdout.read(BUFSIZE)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
|
||||
|
||||
def close(self):
|
||||
''' terminate the connection; nothing to do here '''
|
||||
super(Connection, self).close()
|
||||
self._connected = False
|
|
@ -1,302 +0,0 @@
|
|||
# (c) 2015, Jonathan Davila <jonathan(at)davila.io>
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
lookup: hashi_vault
|
||||
author: Jonathan Davila <jdavila(at)ansible.com>
|
||||
version_added: "2.0"
|
||||
short_description: retrieve secrets from HashiCorp's vault
|
||||
requirements:
|
||||
- hvac (python library)
|
||||
description:
|
||||
- retrieve secrets from HashiCorp's vault
|
||||
notes:
|
||||
- Due to a current limitation in the HVAC library there won't necessarily be an error if a bad endpoint is specified.
|
||||
- As of Ansible 2.10, only the latest secret is returned when specifying a KV v2 path.
|
||||
options:
|
||||
secret:
|
||||
description: query you are making.
|
||||
required: True
|
||||
token:
|
||||
description: vault token.
|
||||
env:
|
||||
- name: VAULT_TOKEN
|
||||
url:
|
||||
description: URL to vault service.
|
||||
env:
|
||||
- name: VAULT_ADDR
|
||||
default: 'http://127.0.0.1:8200'
|
||||
username:
|
||||
description: Authentication user name.
|
||||
password:
|
||||
description: Authentication password.
|
||||
role_id:
|
||||
description: Role id for a vault AppRole auth.
|
||||
env:
|
||||
- name: VAULT_ROLE_ID
|
||||
secret_id:
|
||||
description: Secret id for a vault AppRole auth.
|
||||
env:
|
||||
- name: VAULT_SECRET_ID
|
||||
auth_method:
|
||||
description:
|
||||
- Authentication method to be used.
|
||||
- C(userpass) is added in version 2.8.
|
||||
env:
|
||||
- name: VAULT_AUTH_METHOD
|
||||
choices:
|
||||
- userpass
|
||||
- ldap
|
||||
- approle
|
||||
mount_point:
|
||||
description: vault mount point, only required if you have a custom mount point.
|
||||
default: ldap
|
||||
ca_cert:
|
||||
description: path to certificate to use for authentication.
|
||||
aliases: [ cacert ]
|
||||
validate_certs:
|
||||
description: controls verification and validation of SSL certificates, mostly you only want to turn off with self signed ones.
|
||||
type: boolean
|
||||
default: True
|
||||
namespace:
|
||||
version_added: "2.8"
|
||||
description: namespace where secrets reside. requires HVAC 0.7.0+ and Vault 0.11+.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello:value token=c975b780-d1be-8016-866b-01d0f9b688a5 url=http://myvault:8200')}}"
|
||||
|
||||
- name: Return all secrets from a path
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello token=c975b780-d1be-8016-866b-01d0f9b688a5 url=http://myvault:8200')}}"
|
||||
|
||||
- name: Vault that requires authentication via LDAP
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello:value auth_method=ldap mount_point=ldap username=myuser password=mypas url=http://myvault:8200')}}"
|
||||
|
||||
- name: Vault that requires authentication via username and password
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello:value auth_method=userpass username=myuser password=mypas url=http://myvault:8200')}}"
|
||||
|
||||
- name: Using an ssl vault
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hola:value token=c975b780-d1be-8016-866b-01d0f9b688a5 url=https://myvault:8200 validate_certs=False')}}"
|
||||
|
||||
- name: using certificate auth
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hi:value token=xxxx-xxx-xxx url=https://myvault:8200 validate_certs=True cacert=/cacert/path/ca.pem')}}"
|
||||
|
||||
- name: authenticate with a Vault app role
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello:value auth_method=approle role_id=myroleid secret_id=mysecretid url=http://myvault:8200')}}"
|
||||
|
||||
- name: Return all secrets from a path in a namespace
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/hello token=c975b780-d1be-8016-866b-01d0f9b688a5 url=http://myvault:8200 namespace=teama/admins')}}"
|
||||
|
||||
# When using KV v2 the PATH should include "data" between the secret engine mount and path (e.g. "secret/data/:path")
|
||||
# see: https://www.vaultproject.io/api/secret/kv/kv-v2.html#read-secret-version
|
||||
- name: Return latest KV v2 secret from path
|
||||
debug:
|
||||
msg: "{{ lookup('hashi_vault', 'secret=secret/data/hello token=my_vault_token url=http://myvault_url:8200') }}"
|
||||
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description:
|
||||
- secrets(s) requested
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
HAS_HVAC = False
|
||||
try:
|
||||
import hvac
|
||||
HAS_HVAC = True
|
||||
except ImportError:
|
||||
HAS_HVAC = False
|
||||
|
||||
|
||||
ANSIBLE_HASHI_VAULT_ADDR = 'http://127.0.0.1:8200'
|
||||
|
||||
if os.getenv('VAULT_ADDR') is not None:
|
||||
ANSIBLE_HASHI_VAULT_ADDR = os.environ['VAULT_ADDR']
|
||||
|
||||
|
||||
class HashiVault:
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
self.url = kwargs.get('url', ANSIBLE_HASHI_VAULT_ADDR)
|
||||
self.namespace = kwargs.get('namespace', None)
|
||||
self.avail_auth_method = ['approle', 'userpass', 'ldap']
|
||||
|
||||
# split secret arg, which has format 'secret/hello:value' into secret='secret/hello' and secret_field='value'
|
||||
s = kwargs.get('secret')
|
||||
if s is None:
|
||||
raise AnsibleError("No secret specified for hashi_vault lookup")
|
||||
|
||||
s_f = s.rsplit(':', 1)
|
||||
self.secret = s_f[0]
|
||||
if len(s_f) >= 2:
|
||||
self.secret_field = s_f[1]
|
||||
else:
|
||||
self.secret_field = ''
|
||||
|
||||
self.verify = self.boolean_or_cacert(kwargs.get('validate_certs', True), kwargs.get('cacert', ''))
|
||||
|
||||
# If a particular backend is asked for (and its method exists) we call it, otherwise drop through to using
|
||||
# token auth. This means if a particular auth backend is requested and a token is also given, then we
|
||||
# ignore the token and attempt authentication against the specified backend.
|
||||
#
|
||||
# to enable a new auth backend, simply add a new 'def auth_<type>' method below.
|
||||
#
|
||||
self.auth_method = kwargs.get('auth_method', os.environ.get('VAULT_AUTH_METHOD'))
|
||||
self.verify = self.boolean_or_cacert(kwargs.get('validate_certs', True), kwargs.get('cacert', ''))
|
||||
if self.auth_method and self.auth_method != 'token':
|
||||
try:
|
||||
if self.namespace is not None:
|
||||
self.client = hvac.Client(url=self.url, verify=self.verify, namespace=self.namespace)
|
||||
else:
|
||||
self.client = hvac.Client(url=self.url, verify=self.verify)
|
||||
# prefixing with auth_ to limit which methods can be accessed
|
||||
getattr(self, 'auth_' + self.auth_method)(**kwargs)
|
||||
except AttributeError:
|
||||
raise AnsibleError("Authentication method '%s' not supported."
|
||||
" Available options are %r" % (self.auth_method, self.avail_auth_method))
|
||||
else:
|
||||
self.token = kwargs.get('token', os.environ.get('VAULT_TOKEN', None))
|
||||
if self.token is None and os.environ.get('HOME'):
|
||||
token_filename = os.path.join(
|
||||
os.environ.get('HOME'),
|
||||
'.vault-token'
|
||||
)
|
||||
if os.path.exists(token_filename):
|
||||
with open(token_filename) as token_file:
|
||||
self.token = token_file.read().strip()
|
||||
|
||||
if self.token is None:
|
||||
raise AnsibleError("No Vault Token specified")
|
||||
|
||||
if self.namespace is not None:
|
||||
self.client = hvac.Client(url=self.url, token=self.token, verify=self.verify, namespace=self.namespace)
|
||||
else:
|
||||
self.client = hvac.Client(url=self.url, token=self.token, verify=self.verify)
|
||||
|
||||
if not self.client.is_authenticated():
|
||||
raise AnsibleError("Invalid Hashicorp Vault Token Specified for hashi_vault lookup")
|
||||
|
||||
def get(self):
|
||||
data = self.client.read(self.secret)
|
||||
|
||||
# Check response for KV v2 fields and flatten nested secret data.
|
||||
#
|
||||
# https://vaultproject.io/api/secret/kv/kv-v2.html#sample-response-1
|
||||
try:
|
||||
# sentinel field checks
|
||||
check_dd = data['data']['data']
|
||||
check_md = data['data']['metadata']
|
||||
# unwrap nested data
|
||||
data = data['data']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if data is None:
|
||||
raise AnsibleError("The secret %s doesn't seem to exist for hashi_vault lookup" % self.secret)
|
||||
|
||||
if self.secret_field == '':
|
||||
return data['data']
|
||||
|
||||
if self.secret_field not in data['data']:
|
||||
raise AnsibleError("The secret %s does not contain the field '%s'. for hashi_vault lookup" % (self.secret, self.secret_field))
|
||||
|
||||
return data['data'][self.secret_field]
|
||||
|
||||
def check_params(self, **kwargs):
|
||||
username = kwargs.get('username')
|
||||
if username is None:
|
||||
raise AnsibleError("Authentication method %s requires a username" % self.auth_method)
|
||||
|
||||
password = kwargs.get('password')
|
||||
if password is None:
|
||||
raise AnsibleError("Authentication method %s requires a password" % self.auth_method)
|
||||
|
||||
mount_point = kwargs.get('mount_point')
|
||||
|
||||
return username, password, mount_point
|
||||
|
||||
def auth_userpass(self, **kwargs):
|
||||
username, password, mount_point = self.check_params(**kwargs)
|
||||
if mount_point is None:
|
||||
mount_point = 'userpass'
|
||||
|
||||
self.client.auth_userpass(username, password, mount_point=mount_point)
|
||||
|
||||
def auth_ldap(self, **kwargs):
|
||||
username, password, mount_point = self.check_params(**kwargs)
|
||||
if mount_point is None:
|
||||
mount_point = 'ldap'
|
||||
|
||||
self.client.auth.ldap.login(username, password, mount_point=mount_point)
|
||||
|
||||
def boolean_or_cacert(self, validate_certs, cacert):
|
||||
validate_certs = boolean(validate_certs, strict=False)
|
||||
'''' return a bool or cacert '''
|
||||
if validate_certs is True:
|
||||
if cacert != '':
|
||||
return cacert
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def auth_approle(self, **kwargs):
|
||||
role_id = kwargs.get('role_id', os.environ.get('VAULT_ROLE_ID', None))
|
||||
if role_id is None:
|
||||
raise AnsibleError("Authentication method app role requires a role_id")
|
||||
|
||||
secret_id = kwargs.get('secret_id', os.environ.get('VAULT_SECRET_ID', None))
|
||||
if secret_id is None:
|
||||
raise AnsibleError("Authentication method app role requires a secret_id")
|
||||
|
||||
self.client.auth_approle(role_id, secret_id)
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
if not HAS_HVAC:
|
||||
raise AnsibleError("Please pip install hvac to use the hashi_vault lookup module.")
|
||||
|
||||
vault_args = terms[0].split()
|
||||
vault_dict = {}
|
||||
ret = []
|
||||
|
||||
for param in vault_args:
|
||||
try:
|
||||
key, value = param.split('=')
|
||||
except ValueError:
|
||||
raise AnsibleError("hashi_vault lookup plugin needs key=value pairs, but received %s" % terms)
|
||||
vault_dict[key] = value
|
||||
|
||||
if 'ca_cert' in vault_dict.keys():
|
||||
vault_dict['cacert'] = vault_dict['ca_cert']
|
||||
vault_dict.pop('ca_cert', None)
|
||||
|
||||
vault_conn = HashiVault(**vault_dict)
|
||||
|
||||
for term in terms:
|
||||
key = term.split()[0]
|
||||
value = vault_conn.get()
|
||||
ret.append(value)
|
||||
|
||||
return ret
|
|
@ -1,266 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2012, Derek Carter<goozbach@friocorte.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'core'
|
||||
}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: selinux
|
||||
short_description: Change policy and state of SELinux
|
||||
description:
|
||||
- Configures the SELinux mode and policy.
|
||||
- A reboot may be required after usage.
|
||||
- Ansible will not issue this reboot but will let you know when it is required.
|
||||
version_added: "0.7"
|
||||
options:
|
||||
policy:
|
||||
description:
|
||||
- The name of the SELinux policy to use (e.g. C(targeted)) will be required if state is not C(disabled).
|
||||
state:
|
||||
description:
|
||||
- The SELinux mode.
|
||||
required: true
|
||||
choices: [ disabled, enforcing, permissive ]
|
||||
configfile:
|
||||
description:
|
||||
- The path to the SELinux configuration file, if non-standard.
|
||||
default: /etc/selinux/config
|
||||
aliases: [ conf, file ]
|
||||
requirements: [ libselinux-python ]
|
||||
author:
|
||||
- Derek Carter (@goozbach) <goozbach@friocorte.com>
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Enable SELinux
|
||||
selinux:
|
||||
policy: targeted
|
||||
state: enforcing
|
||||
|
||||
- name: Put SELinux in permissive mode, logging actions that would be blocked.
|
||||
selinux:
|
||||
policy: targeted
|
||||
state: permissive
|
||||
|
||||
- name: Disable SELinux
|
||||
selinux:
|
||||
state: disabled
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
msg:
|
||||
description: Messages that describe changes that were made.
|
||||
returned: always
|
||||
type: str
|
||||
sample: Config SELinux state changed from 'disabled' to 'permissive'
|
||||
configfile:
|
||||
description: Path to SELinux configuration file.
|
||||
returned: always
|
||||
type: str
|
||||
sample: /etc/selinux/config
|
||||
policy:
|
||||
description: Name of the SELinux policy.
|
||||
returned: always
|
||||
type: str
|
||||
sample: targeted
|
||||
state:
|
||||
description: SELinux mode.
|
||||
returned: always
|
||||
type: str
|
||||
sample: enforcing
|
||||
reboot_required:
|
||||
description: Whether or not an reboot is required for the changes to take effect.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
SELINUX_IMP_ERR = None
|
||||
try:
|
||||
import selinux
|
||||
HAS_SELINUX = True
|
||||
except ImportError:
|
||||
SELINUX_IMP_ERR = traceback.format_exc()
|
||||
HAS_SELINUX = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.facts.utils import get_file_lines
|
||||
|
||||
|
||||
# getter subroutines
|
||||
def get_config_state(configfile):
|
||||
lines = get_file_lines(configfile, strip=False)
|
||||
|
||||
for line in lines:
|
||||
stateline = re.match(r'^SELINUX=.*$', line)
|
||||
if stateline:
|
||||
return line.split('=')[1].strip()
|
||||
|
||||
|
||||
def get_config_policy(configfile):
|
||||
lines = get_file_lines(configfile, strip=False)
|
||||
|
||||
for line in lines:
|
||||
stateline = re.match(r'^SELINUXTYPE=.*$', line)
|
||||
if stateline:
|
||||
return line.split('=')[1].strip()
|
||||
|
||||
|
||||
# setter subroutines
|
||||
def set_config_state(module, state, configfile):
|
||||
# SELINUX=permissive
|
||||
# edit config file with state value
|
||||
stateline = 'SELINUX=%s' % state
|
||||
lines = get_file_lines(configfile, strip=False)
|
||||
|
||||
tmpfd, tmpfile = tempfile.mkstemp()
|
||||
|
||||
with open(tmpfile, "w") as write_file:
|
||||
for line in lines:
|
||||
write_file.write(re.sub(r'^SELINUX=.*', stateline, line) + '\n')
|
||||
|
||||
module.atomic_move(tmpfile, configfile)
|
||||
|
||||
|
||||
def set_state(module, state):
|
||||
if state == 'enforcing':
|
||||
selinux.security_setenforce(1)
|
||||
elif state == 'permissive':
|
||||
selinux.security_setenforce(0)
|
||||
elif state == 'disabled':
|
||||
pass
|
||||
else:
|
||||
msg = 'trying to set invalid runtime state %s' % state
|
||||
module.fail_json(msg=msg)
|
||||
|
||||
|
||||
def set_config_policy(module, policy, configfile):
|
||||
if not os.path.exists('/etc/selinux/%s/policy' % policy):
|
||||
module.fail_json(msg='Policy %s does not exist in /etc/selinux/' % policy)
|
||||
|
||||
# edit config file with state value
|
||||
# SELINUXTYPE=targeted
|
||||
policyline = 'SELINUXTYPE=%s' % policy
|
||||
lines = get_file_lines(configfile, strip=False)
|
||||
|
||||
tmpfd, tmpfile = tempfile.mkstemp()
|
||||
|
||||
with open(tmpfile, "w") as write_file:
|
||||
for line in lines:
|
||||
write_file.write(re.sub(r'^SELINUXTYPE=.*', policyline, line) + '\n')
|
||||
|
||||
module.atomic_move(tmpfile, configfile)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
policy=dict(type='str'),
|
||||
state=dict(type='str', required='True', choices=['enforcing', 'permissive', 'disabled']),
|
||||
configfile=dict(type='str', default='/etc/selinux/config', aliases=['conf', 'file']),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not HAS_SELINUX:
|
||||
module.fail_json(msg=missing_required_lib('libselinux-python'), exception=SELINUX_IMP_ERR)
|
||||
|
||||
# global vars
|
||||
changed = False
|
||||
msgs = []
|
||||
configfile = module.params['configfile']
|
||||
policy = module.params['policy']
|
||||
state = module.params['state']
|
||||
runtime_enabled = selinux.is_selinux_enabled()
|
||||
runtime_policy = selinux.selinux_getpolicytype()[1]
|
||||
runtime_state = 'disabled'
|
||||
reboot_required = False
|
||||
|
||||
if runtime_enabled:
|
||||
# enabled means 'enforcing' or 'permissive'
|
||||
if selinux.security_getenforce():
|
||||
runtime_state = 'enforcing'
|
||||
else:
|
||||
runtime_state = 'permissive'
|
||||
|
||||
if not os.path.isfile(configfile):
|
||||
module.fail_json(msg="Unable to find file {0}".format(configfile),
|
||||
details="Please install SELinux-policy package, "
|
||||
"if this package is not installed previously.")
|
||||
|
||||
config_policy = get_config_policy(configfile)
|
||||
config_state = get_config_state(configfile)
|
||||
|
||||
# check to see if policy is set if state is not 'disabled'
|
||||
if state != 'disabled':
|
||||
if not policy:
|
||||
module.fail_json(msg="Policy is required if state is not 'disabled'")
|
||||
else:
|
||||
if not policy:
|
||||
policy = config_policy
|
||||
|
||||
# check changed values and run changes
|
||||
if policy != runtime_policy:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
# cannot change runtime policy
|
||||
msgs.append("Running SELinux policy changed from '%s' to '%s'" % (runtime_policy, policy))
|
||||
changed = True
|
||||
|
||||
if policy != config_policy:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
set_config_policy(module, policy, configfile)
|
||||
msgs.append("SELinux policy configuration in '%s' changed from '%s' to '%s'" % (configfile, config_policy, policy))
|
||||
changed = True
|
||||
|
||||
if state != runtime_state:
|
||||
if runtime_enabled:
|
||||
if state == 'disabled':
|
||||
if runtime_state != 'permissive':
|
||||
# Temporarily set state to permissive
|
||||
if not module.check_mode:
|
||||
set_state(module, 'permissive')
|
||||
module.warn("SELinux state temporarily changed from '%s' to 'permissive'. State change will take effect next reboot." % (runtime_state))
|
||||
changed = True
|
||||
else:
|
||||
module.warn('SELinux state change will take effect next reboot')
|
||||
reboot_required = True
|
||||
else:
|
||||
if not module.check_mode:
|
||||
set_state(module, state)
|
||||
msgs.append("SELinux state changed from '%s' to '%s'" % (runtime_state, state))
|
||||
|
||||
# Only report changes if the file is changed.
|
||||
# This prevents the task from reporting changes every time the task is run.
|
||||
changed = True
|
||||
else:
|
||||
module.warn("Reboot is required to set SELinux state to '%s'" % state)
|
||||
reboot_required = True
|
||||
|
||||
if state != config_state:
|
||||
if not module.check_mode:
|
||||
set_config_state(module, state, configfile)
|
||||
msgs.append("Config SELinux state changed from '%s' to '%s'" % (config_state, state))
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed, msg=', '.join(msgs), configfile=configfile, policy=policy, state=state, reboot_required=reboot_required)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,257 +0,0 @@
|
|||
#!powershell
|
||||
|
||||
# Copyright: (c) 2018, Micah Hunsberger (@mhunsber)
|
||||
# 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
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
$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)){
|
||||
$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"
|
||||
|
||||
Function Get-CommentIndex($line) {
|
||||
$c_index = $line.IndexOf('#')
|
||||
if($c_index -lt 0) {
|
||||
$c_index = $line.Length
|
||||
}
|
||||
return $c_index
|
||||
}
|
||||
|
||||
Function Get-HostEntryParts($line) {
|
||||
$success = $true
|
||||
$c_index = Get-CommentIndex -line $line
|
||||
$pure_line = $line.Substring(0,$c_index).Trim()
|
||||
$bits = $pure_line -split "\s+"
|
||||
if($bits.Length -lt 2){
|
||||
return @{
|
||||
success = $false
|
||||
ip_address = ""
|
||||
ip_type = ""
|
||||
canonical_name = ""
|
||||
aliases = @()
|
||||
}
|
||||
}
|
||||
$ip_obj = [ipaddress]::None
|
||||
if(-not [ipaddress]::TryParse($bits[0], [ref]$ip_obj) ){
|
||||
$success = $false
|
||||
}
|
||||
$cname = $bits[1]
|
||||
$als = New-Object string[] ($bits.Length - 2)
|
||||
[array]::Copy($bits, 2, $als, 0, $als.Length)
|
||||
return @{
|
||||
success = $success
|
||||
ip_address = $ip_obj.IPAddressToString
|
||||
ip_type = $ip_obj.AddressFamily
|
||||
canonical_name = $cname
|
||||
aliases = $als
|
||||
}
|
||||
}
|
||||
|
||||
Function Find-HostName($line, $name) {
|
||||
$c_idx = Get-CommentIndex -line $line
|
||||
$re = New-Object regex ("\s+$($name.Replace('.',"\."))(\s|$)", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
||||
$match = $re.Match($line, 0, $c_idx)
|
||||
return $match
|
||||
}
|
||||
|
||||
Function Remove-HostEntry($list, $idx) {
|
||||
$module.Result.changed = $true
|
||||
$list.RemoveAt($idx)
|
||||
}
|
||||
|
||||
Function Add-HostEntry($list, $cname, $aliases, $ip) {
|
||||
$module.Result.changed = $true
|
||||
$line = "$ip $cname $($aliases -join ' ')"
|
||||
$list.Add($line) | Out-Null
|
||||
}
|
||||
|
||||
Function Remove-HostnamesFromEntry($list, $idx, $aliases) {
|
||||
$line = $list[$idx]
|
||||
$line_removed = $false
|
||||
|
||||
foreach($name in $aliases){
|
||||
$match = Find-HostName -line $line -name $name
|
||||
if($match.Success){
|
||||
$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") {
|
||||
$list.RemoveAt($idx)
|
||||
$line_removed = $true
|
||||
# we're done
|
||||
return @{
|
||||
line_removed = $line_removed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($line -ne $list[$idx]){
|
||||
$module.Result.changed = $true
|
||||
$list[$idx] = $line
|
||||
}
|
||||
return @{
|
||||
line_removed = $line_removed
|
||||
}
|
||||
}
|
||||
|
||||
Function Add-AliasesToEntry($list, $idx, $aliases) {
|
||||
$line = $list[$idx]
|
||||
foreach($name in $aliases){
|
||||
$match = Find-HostName -line $line -name $name
|
||||
if(-not $match.Success) {
|
||||
# just add the alias before the comment
|
||||
$line = $line.Insert((Get-CommentIndex -line $line), " $name ")
|
||||
}
|
||||
}
|
||||
if($line -ne $list[$idx]){
|
||||
$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
|
||||
for($idx = 0; $idx -lt $hosts_lines.Count; $idx++) {
|
||||
$entry = $hosts_lines[$idx]
|
||||
# skip comment lines
|
||||
if(-not $entry.Trim().StartsWith('#')) {
|
||||
$entry_parts = Get-HostEntryParts -line $entry
|
||||
if($entry_parts.success) {
|
||||
if(-not $ip_address -or $entry_parts.ip_address -eq $ip_address) {
|
||||
if(-not $canonical_name -or $entry_parts.canonical_name -eq $canonical_name) {
|
||||
if(Remove-HostEntry -list $hosts_lines -idx $idx){
|
||||
# keep index correct if we removed the line
|
||||
$idx = $idx - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($state -eq 'present') {
|
||||
$entry_idx = -1
|
||||
$aliases_to_keep = @()
|
||||
# go through lines, find the entry and determine what to remove based on action
|
||||
for($idx = 0; $idx -lt $hosts_lines.Count; $idx++) {
|
||||
$entry = $hosts_lines[$idx]
|
||||
# skip comment lines
|
||||
if(-not $entry.Trim().StartsWith('#')) {
|
||||
$entry_parts = Get-HostEntryParts -line $entry
|
||||
if($entry_parts.success) {
|
||||
$aliases_to_remove = @()
|
||||
if($entry_parts.ip_address -eq $ip_address) {
|
||||
if($entry_parts.canonical_name -eq $canonical_name) {
|
||||
$entry_idx = $idx
|
||||
|
||||
if($action -eq 'set') {
|
||||
$aliases_to_remove = $entry_parts.aliases | Where-Object { $aliases -notcontains $_ }
|
||||
} elseif($action -eq 'remove') {
|
||||
$aliases_to_remove = $aliases
|
||||
}
|
||||
} else {
|
||||
# this is the right ip_address, but not the cname we were looking for.
|
||||
# we need to make sure none of aliases or canonical_name exist for this entry
|
||||
# since the given canonical_name should be an A/AAAA record,
|
||||
# and aliases should be cname records for the canonical_name.
|
||||
$aliases_to_remove = $aliases + $canonical_name
|
||||
}
|
||||
} else {
|
||||
# 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-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") {
|
||||
$aliases_to_remove = $canonical_name
|
||||
} elseif ($aliases -contains $entry_parts.canonical_name) {
|
||||
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 {
|
||||
$aliases_to_remove = $aliases + $canonical_name
|
||||
}
|
||||
} else {
|
||||
# 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) {
|
||||
$idx = $idx - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($entry_idx -ge 0) {
|
||||
$aliases_to_add = @()
|
||||
$entry_parts = Get-HostEntryParts -line $hosts_lines[$entry_idx]
|
||||
if($action -eq 'remove') {
|
||||
$aliases_to_add = $aliases_to_keep | Where-Object { $entry_parts.aliases -notcontains $_ }
|
||||
} else {
|
||||
$aliases_to_add = ($aliases + $aliases_to_keep) | Where-Object { $entry_parts.aliases -notcontains $_ }
|
||||
}
|
||||
|
||||
if($aliases_to_add) {
|
||||
Add-AliasesToEntry -list $hosts_lines -idx $entry_idx -aliases $aliases_to_add
|
||||
}
|
||||
} else {
|
||||
# add the entry at the end
|
||||
if($action -eq 'remove') {
|
||||
if($aliases_to_keep) {
|
||||
Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name -aliases $aliases_to_keep
|
||||
} else {
|
||||
Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name
|
||||
}
|
||||
} else {
|
||||
Add-HostEntry -list $hosts_lines -ip $ip_address -cname $canonical_name -aliases ($aliases + $aliases_to_keep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
|
@ -1,126 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2018, 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_hosts
|
||||
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.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether the entry should be present or absent.
|
||||
- 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 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 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 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 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 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 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 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'''
|
||||
- name: Add 127.0.0.1 as an A record for localhost
|
||||
win_hosts:
|
||||
state: present
|
||||
canonical_name: localhost
|
||||
ip_address: 127.0.0.1
|
||||
|
||||
- name: Add ::1 as an AAAA record for localhost
|
||||
win_hosts:
|
||||
state: present
|
||||
canonical_name: localhost
|
||||
ip_address: '::1'
|
||||
|
||||
- name: Remove 'bar' and 'zed' from the list of aliases for foo (192.168.1.100)
|
||||
win_hosts:
|
||||
state: present
|
||||
canoncial_name: foo
|
||||
ip_address: 192.168.1.100
|
||||
action: remove
|
||||
aliases:
|
||||
- bar
|
||||
- zed
|
||||
|
||||
- name: Remove hosts entries with canonical name 'bar'
|
||||
win_hosts:
|
||||
state: absent
|
||||
canonical_name: bar
|
||||
|
||||
- name: Remove 10.2.0.1 from the list of hosts
|
||||
win_hosts:
|
||||
state: absent
|
||||
ip_address: 10.2.0.1
|
||||
|
||||
- name: Ensure all name resolution is handled by DNS
|
||||
win_hosts:
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
'''
|
Loading…
Reference in a new issue