ipahost: Add support for several IP addresses and also to change them

ipahost was so far ignoring IP addresses when the host already existed.
This happened because host_mod is not providing functionality to do this.
Now ipaddress is a list and it is possible to ensure a host with several
IP addresses (these can be IPv4 and IPv6). Also it is possible to ensure
presence and absence of IP addresses for an exising host using action
member.

There are no IP address conclict checks as this would lead into issues with
updating an existing host that already is using a duplicate IP address for
example for round-robin (RR). Also this might lead into issues with ensuring
a new host with several IP addresses in this case. Also to ensure a list of
hosts with changing the IP address of one host to another in the list would
result in issues here.

New example playbooks have been added:

    playbooks/host/host-present-with-several-ip-addresses.yml
    playbooks/host/host-member-ipaddresses-absent.yml
    playbooks/host/host-member-ipaddresses-present.yml

A new test has been added for verification:

    tests/host/test_host_ipaddresses.yml

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1783976
       https://bugzilla.redhat.com/show_bug.cgi?id=1783979
This commit is contained in:
Thomas Woerner
2020-02-12 16:54:13 +01:00
parent 84aab60dd3
commit 167c76311d
7 changed files with 600 additions and 50 deletions

View File

@@ -65,6 +65,79 @@ Example playbook to ensure host presence:
- "52:54:00:BD:97:1E" - "52:54:00:BD:97:1E"
state: present state: present
``` ```
Compared to `ipa host-add` command no IP address conflict check is done as the ipahost module supports to have several IPv4 and IPv6 addresses for a host.
Example playbook to ensure host presence with several IP addresses:
```yaml
---
- name: Playbook to handle hosts
hosts: ipaserver
become: true
tasks:
# Ensure host is present
- ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
description: Example host
ip_address:
- 192.168.0.123
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b3
- fe80::20c:29ff:fe02:a1b4
locality: Lab
ns_host_location: Lab
ns_os_version: CentOS 7
ns_hardware_platform: Lenovo T61
mac_address:
- "08:00:27:E3:B1:2D"
- "52:54:00:BD:97:1E"
state: present
```
Example playbook to ensure IP addresses are present for a host:
```yaml
---
- name: Playbook to handle hosts
hosts: ipaserver
become: true
tasks:
# Ensure host is present
- ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
ip_address:
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b4
action: member
state: present
```
Example playbook to ensure IP addresses are absent for a host:
```yaml
---
- name: Playbook to handle hosts
hosts: ipaserver
become: true
tasks:
# Ensure host is present
- ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
ip_address:
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b4
action: member
state: absent
```
Example playbook to ensure host presence without DNS: Example playbook to ensure host presence without DNS:
@@ -215,7 +288,7 @@ Example playbook to disable a host:
update_dns: yes update_dns: yes
state: disabled state: disabled
``` ```
`update_dns` controls if the DNS entries will be updated. `update_dns` controls if the DNS entries will be updated in this case. For `state` present it is controlling the update of the DNS SSHFP records, but not the the other DNS records.
Example playbook to ensure a host is absent: Example playbook to ensure a host is absent:
@@ -286,8 +359,8 @@ Variable | Description | Required
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no `ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
`force` | Force host name even if not in DNS. | no `force` | Force host name even if not in DNS. | no
`reverse` | Reverse DNS detection. | no `reverse` | Reverse DNS detection. | no
`ip_address` \| `ipaddress` | The host IP address. | no `ip_address` \| `ipaddress` | The host IP address list. It can contain IPv4 and IPv6 addresses. No conflict check for IP addresses is done. | no
`update_dns` | Update DNS entries. | no `update_dns` | For existing hosts: DNS SSHFP records are updated with `state` present and all DNS entries for a host removed with `state` absent. | no
Return Values Return Values

View File

@@ -0,0 +1,17 @@
---
- name: Host member IP addresses absent
hosts: ipaserver
become: true
tasks:
- name: Ensure host01.example.com IP addresses absent
ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
ip_address:
- 192.168.0.123
- fe80::20c:29ff:fe02:a1b3
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b4
action: member
state: absent

View File

@@ -0,0 +1,16 @@
---
- name: Host member IP addresses present
hosts: ipaserver
become: true
tasks:
- name: Ensure host01.example.com IP addresses present
ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
ip_address:
- 192.168.0.123
- fe80::20c:29ff:fe02:a1b3
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b4
action: member

View File

@@ -0,0 +1,24 @@
---
- name: Host present with several IP addresses
hosts: ipaserver
become: true
tasks:
- name: Ensure host is present
ipahost:
ipaadmin_password: MyPassword123
name: host01.example.com
description: Example host
ip_address:
- 192.168.0.123
- fe80::20c:29ff:fe02:a1b3
- 192.168.0.124
- fe80::20c:29ff:fe02:a1b4
locality: Lab
ns_host_location: Lab
ns_os_version: CentOS 7
ns_hardware_platform: Lenovo T61
mac_address:
- "08:00:27:E3:B1:2D"
- "52:54:00:BD:97:1E"
state: present

View File

@@ -42,6 +42,7 @@ try:
from ipalib.x509 import Encoding from ipalib.x509 import Encoding
except ImportError: except ImportError:
from cryptography.hazmat.primitives.serialization import Encoding from cryptography.hazmat.primitives.serialization import Encoding
import socket
import base64 import base64
import six import six
@@ -285,3 +286,25 @@ def encode_certificate(cert):
if not six.PY2: if not six.PY2:
encoded = encoded.decode('ascii') encoded = encoded.decode('ascii')
return encoded return encoded
def is_ipv4_addr(ipaddr):
"""
Test if figen IP address is a valid IPv4 address
"""
try:
socket.inet_pton(socket.AF_INET, ipaddr)
except socket.error:
return False
return True
def is_ipv6_addr(ipaddr):
"""
Test if figen IP address is a valid IPv6 address
"""
try:
socket.inet_pton(socket.AF_INET6, ipaddr)
except socket.error:
return False
return True

View File

@@ -176,11 +176,16 @@ options:
default: true default: true
required: false required: false
ip_address: ip_address:
description: The host IP address description:
The host IP address list (IPv4 and IPv6). No IP address conflict
check will be done.
aliases: ["ipaddress"] aliases: ["ipaddress"]
required: false required: false
update_dns: update_dns:
description: Update DNS entries description:
Controls the update of the DNS SSHFP records for existing hosts and
the removal of all DNS entries if a host gets removed with state
absent.
required: false required: false
description: description:
description: The host description description: The host description
@@ -306,11 +311,16 @@ options:
default: true default: true
required: false required: false
ip_address: ip_address:
description: The host IP address description:
The host IP address list (IPv4 and IPv6). No IP address conflict
check will be done.
aliases: ["ipaddress"] aliases: ["ipaddress"]
required: false required: false
update_dns: update_dns:
description: Update DNS entries description:
Controls the update of the DNS SSHFP records for existing hosts and
the removal of all DNS entries if a host gets removed with state
absent.
required: false required: false
update_password: update_password:
description: description:
@@ -398,7 +408,8 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
module_params_get, gen_add_del_lists, encode_certificate, api_get_realm module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \
is_ipv4_addr, is_ipv6_addr
import six import six
@@ -428,6 +439,32 @@ def find_host(module, name):
return None return None
def find_dnsrecord(module, name):
domain_name = name[name.find(".")+1:]
host_name = name[:name.find(".")]
_args = {
"all": True,
"idnsname": to_text(host_name),
}
_result = api_command(module, "dnsrecord_find", to_text(domain_name),
_args)
if len(_result["result"]) > 1:
module.fail_json(
msg="There is more than one host '%s'" % (name))
elif len(_result["result"]) == 1:
_res = _result["result"][0]
certs = _res.get("usercertificate")
if certs is not None:
_res["usercertificate"] = [encode_certificate(cert) for
cert in certs]
return _res
else:
return None
def show_host(module, name): def show_host(module, name):
_result = api_command(module, "host_show", to_text(name), {}) _result = api_command(module, "host_show", to_text(name), {})
return _result["result"] return _result["result"]
@@ -470,16 +507,34 @@ def gen_args(description, locality, location, platform, os, password, random,
_args["ipakrboktoauthasdelegate"] = ok_to_auth_as_delegate _args["ipakrboktoauthasdelegate"] = ok_to_auth_as_delegate
if force is not None: if force is not None:
_args["force"] = force _args["force"] = force
if reverse is not None:
_args["no_reverse"] = not reverse
if ip_address is not None: if ip_address is not None:
_args["ip_address"] = ip_address # IP addresses are handed extra, therefore it is needed to set
# the force option here to make sure that host-add is able to
# add a host without IP address.
_args["force"] = True
if update_dns is not None: if update_dns is not None:
_args["updatedns"] = update_dns _args["updatedns"] = update_dns
return _args return _args
def gen_dnsrecord_args(module, ip_address, reverse):
_args = {}
if reverse is not None:
_args["a_extra_create_reverse"] = reverse
_args["aaaa_extra_create_reverse"] = reverse
if ip_address is not None:
for ip in ip_address:
if is_ipv4_addr(ip):
_args.setdefault("arecord", []).append(ip)
elif is_ipv6_addr(ip):
_args.setdefault("aaaarecord", []).append(ip)
else:
module.fail_json(msg="'%s' is not a valid IP address." % ip)
return _args
def check_parameters( def check_parameters(
module, state, action, module, state, action,
description, locality, location, platform, os, password, random, description, locality, location, platform, os, password, random,
@@ -499,8 +554,7 @@ def check_parameters(
"os", "password", "random", "mac_address", "sshpubkey", "os", "password", "random", "mac_address", "sshpubkey",
"userclass", "auth_ind", "requires_pre_auth", "userclass", "auth_ind", "requires_pre_auth",
"ok_as_delegate", "ok_to_auth_as_delegate", "force", "ok_as_delegate", "ok_to_auth_as_delegate", "force",
"reverse", "ip_address", "update_dns", "reverse", "update_dns", "update_password"]
"update_password"]
for x in invalid: for x in invalid:
if vars()[x] is not None: if vars()[x] is not None:
module.fail_json( module.fail_json(
@@ -512,7 +566,7 @@ def check_parameters(
"password", "random", "mac_address", "sshpubkey", "password", "random", "mac_address", "sshpubkey",
"userclass", "auth_ind", "requires_pre_auth", "userclass", "auth_ind", "requires_pre_auth",
"ok_as_delegate", "ok_to_auth_as_delegate", "force", "ok_as_delegate", "ok_to_auth_as_delegate", "force",
"reverse", "ip_address", "update_password"] "reverse", "update_password"]
for x in invalid: for x in invalid:
if vars()[x] is not None: if vars()[x] is not None:
module.fail_json( module.fail_json(
@@ -549,9 +603,6 @@ def main():
default=None, no_log=True), default=None, no_log=True),
random=dict(type="bool", aliases=["random_password"], random=dict(type="bool", aliases=["random_password"],
default=None), default=None),
certificate=dict(type="list", aliases=["usercertificate"], certificate=dict(type="list", aliases=["usercertificate"],
default=None), default=None),
managedby_host=dict(type="list", managedby_host=dict(type="list",
@@ -608,7 +659,7 @@ def main():
default=None), default=None),
force=dict(type='bool', default=None), force=dict(type='bool', default=None),
reverse=dict(type='bool', default=None), reverse=dict(type='bool', default=None),
ip_address=dict(type="str", aliases=["ipaddress"], ip_address=dict(type="list", aliases=["ipaddress"],
default=None), default=None),
update_dns=dict(type="bool", aliases=["updatedns"], update_dns=dict(type="bool", aliases=["updatedns"],
default=None), default=None),
@@ -820,6 +871,7 @@ def main():
# Make sure host exists # Make sure host exists
res_find = find_host(ansible_module, name) res_find = find_host(ansible_module, name)
res_find_dnsrecord = find_dnsrecord(ansible_module, name)
# Create command # Create command
if state == "present": if state == "present":
@@ -829,6 +881,8 @@ def main():
random, mac_address, sshpubkey, userclass, auth_ind, random, mac_address, sshpubkey, userclass, auth_ind,
requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate, requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate,
force, reverse, ip_address, update_dns) force, reverse, ip_address, update_dns)
dnsrecord_args = gen_dnsrecord_args(
ansible_module, ip_address, reverse)
if action == "host": if action == "host":
# Found the host # Found the host
@@ -938,39 +992,20 @@ def main():
res_find.get( res_find.get(
"ipaallowedtoperform_read_keys_hostgroup")) "ipaallowedtoperform_read_keys_hostgroup"))
else: # IP addresses are not really a member of hosts, but
certificate_add = certificate or [] # we will simply treat it as this to enable the
certificate_del = [] # addition and removal of IPv4 and IPv6 addresses in
managedby_host_add = managedby_host or [] # a simple way.
managedby_host_del = [] _dnsrec = res_find_dnsrecord or {}
principal_add = principal or [] dnsrecord_a_add, dnsrecord_a_del = gen_add_del_lists(
principal_del = [] dnsrecord_args.get("arecord"),
allow_create_keytab_user_add = \ _dnsrec.get("arecord"))
allow_create_keytab_user or [] dnsrecord_aaaa_add, dnsrecord_aaaa_del = \
allow_create_keytab_user_del = [] gen_add_del_lists(
allow_create_keytab_group_add = \ dnsrecord_args.get("aaaarecord"),
allow_create_keytab_group or [] _dnsrec.get("aaaarecord"))
allow_create_keytab_group_del = []
allow_create_keytab_host_add = \
allow_create_keytab_host or []
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = \
allow_create_keytab_hostgroup or []
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = \
allow_retrieve_keytab_user or []
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = \
allow_retrieve_keytab_group or []
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = \
allow_retrieve_keytab_host or []
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = []
else: if action != "host" or (action == "host" and res_find is None):
certificate_add = certificate or [] certificate_add = certificate or []
certificate_del = [] certificate_del = []
managedby_host_add = managedby_host or [] managedby_host_add = managedby_host or []
@@ -1001,6 +1036,10 @@ def main():
allow_retrieve_keytab_hostgroup_add = \ allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or [] allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = [] allow_retrieve_keytab_hostgroup_del = []
dnsrecord_a_add = dnsrecord_args.get("arecord") or []
dnsrecord_a_del = []
dnsrecord_aaaa_add = dnsrecord_args.get("aaaarecord") or []
dnsrecord_aaaa_del = []
# Remove canonical principal from principal_del # Remove canonical principal from principal_del
canonical_principal = "host/" + name + "@" + server_realm canonical_principal = "host/" + name + "@" + server_realm
@@ -1135,6 +1174,36 @@ def main():
"hostgroup": allow_retrieve_keytab_hostgroup_del, "hostgroup": allow_retrieve_keytab_hostgroup_del,
}]) }])
if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0:
domain_name = name[name.find(".")+1:]
host_name = name[:name.find(".")]
commands.append([domain_name,
"dnsrecord_add",
{
"idnsname": host_name,
"arecord": dnsrecord_a_add,
"a_extra_create_reverse": reverse,
"aaaarecord": dnsrecord_aaaa_add,
"aaaa_extra_create_reverse": reverse
}])
if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
domain_name = name[name.find(".")+1:]
host_name = name[:name.find(".")]
# There seems to be an issue with dnsrecord_del (not
# for dnsrecord_add) if aaaarecord is an empty list.
# Therefore this is done differently here:
_args = {"idnsname": host_name}
if len(dnsrecord_a_del) > 0:
_args["arecord"] = dnsrecord_a_del
if len(dnsrecord_aaaa_del) > 0:
_args["aaaarecord"] = dnsrecord_aaaa_del
commands.append([domain_name,
"dnsrecord_del", _args])
elif state == "absent": elif state == "absent":
if action == "host": if action == "host":
@@ -1215,6 +1284,17 @@ def main():
"hostgroup": allow_retrieve_keytab_hostgroup, "hostgroup": allow_retrieve_keytab_hostgroup,
}]) }])
dnsrecord_args = gen_dnsrecord_args(ansible_module,
ip_address, reverse)
if "arecord" in dnsrecord_args or \
"aaaarecord" in dnsrecord_args:
domain_name = name[name.find(".")+1:]
host_name = name[:name.find(".")]
dnsrecord_args["idnsname"] = host_name
commands.append([domain_name, "dnsrecord_del",
dnsrecord_args])
elif state == "disabled": elif state == "disabled":
if res_find is not None: if res_find is not None:
commands.append([name, "host_disable", {}]) commands.append([name, "host_disable", {}])
@@ -1259,6 +1339,11 @@ def main():
# Host is already disabled, ignore error # Host is already disabled, ignore error
if "This entry is already disabled" in msg: if "This entry is already disabled" in msg:
continue continue
# Ignore no modification error.
if "no modifications to be performed" in msg:
continue
ansible_module.fail_json(msg="%s: %s: %s" % (command, name, ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
msg)) msg))

View File

@@ -0,0 +1,312 @@
---
- name: Test host IP addresses
hosts: ipaserver
become: true
tasks:
- name: Get Domain from server name
set_fact:
ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}"
when: ipaserver_domain is not defined
- name: Set host1_fqdn .. host6_fqdn
set_fact:
host1_fqdn: "{{ 'host1.' + ipaserver_domain }}"
host2_fqdn: "{{ 'host2.' + ipaserver_domain }}"
host3_fqdn: "{{ 'host3.' + ipaserver_domain }}"
- name: Get IPv4 address prefix from server node
set_fact:
ipv4_prefix: "{{ ansible_default_ipv4.address.split('.')[:-1] |
join('.') }}"
- name: Host absent
ipahost:
ipaadmin_password: MyPassword123
name:
- "{{ host1_fqdn }}"
- "{{ host2_fqdn }}"
- "{{ host3_fqdn }}"
update_dns: yes
state: absent
- name: Host "{{ host1_fqdn }}" present
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.201' }}"
- fe80::20c:29ff:fe02:a1b2
update_dns: yes
reverse: no
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" present again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.201' }}"
- fe80::20c:29ff:fe02:a1b2
update_dns: yes
reverse: no
register: result
failed_when: result.changed
- name: Host "{{ host1_fqdn }}" present again with new IP address
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
update_dns: yes
reverse: no
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" present again with new IP address again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
update_dns: yes
reverse: no
register: result
failed_when: result.changed
- name: Host "{{ host1_fqdn }}" member IPv4 address present
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: "{{ ipv4_prefix + '.201' }}"
action: member
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" member IPv4 address present again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: "{{ ipv4_prefix + '.201' }}"
action: member
register: result
failed_when: result.changed
- name: Host "{{ host1_fqdn }}" member IPv4 address absent
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: "{{ ipv4_prefix + '.201' }}"
action: member
state: absent
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" member IPv4 address absent again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: "{{ ipv4_prefix + '.201' }}"
action: member
state: absent
register: result
failed_when: result.changed
- name: Host "{{ host1_fqdn }}" member IPv6 address present
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: fe80::20c:29ff:fe02:a1b2
action: member
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" member IPv6 address present again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: fe80::20c:29ff:fe02:a1b2
action: member
register: result
failed_when: result.changed
- name: Host "{{ host1_fqdn }}" member IPv6 address absent
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: fe80::20c:29ff:fe02:a1b2
action: member
state: absent
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" member IPv6 address absent again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address: fe80::20c:29ff:fe02:a1b2
action: member
state: absent
register: result
- name: Host "{{ host1_fqdn }}" member all ip-addresses absent
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
action: member
state: absent
register: result
failed_when: not result.changed
- name: Host "{{ host1_fqdn }}" all member ip-addresses absent again
ipahost:
ipaadmin_password: MyPassword123
name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
action: member
state: absent
register: result
failed_when: result.changed
- name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
- name: "{{ host2_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: not result.changed
- name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses again
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host1_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
- name: "{{ host2_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: result.changed
- name: Hosts "{{ host3_fqdn }}" present with same IP addresses
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: not result.changed
- name: Hosts "{{ host3_fqdn }}" present with same IP addresses again
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: result.changed
- name: Host "{{ host3_fqdn }}" present with differnt IP addresses
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.111' }}"
- fe80::20c:29ff:fe02:a1b1
- "{{ ipv4_prefix + '.121' }}"
- fe80::20c:29ff:fe02:a1b2
register: result
failed_when: not result.changed
- name: Host "{{ host3_fqdn }}" present with different IP addresses again
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.111' }}"
- fe80::20c:29ff:fe02:a1b1
- "{{ ipv4_prefix + '.121' }}"
- fe80::20c:29ff:fe02:a1b2
register: result
failed_when: result.changed
- name: Host "{{ host3_fqdn }}" present with old IP addresses
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: not result.changed
- name: Host "{{ host3_fqdn }}" present with old IP addresses again
ipahost:
ipaadmin_password: MyPassword123
hosts:
- name: "{{ host3_fqdn }}"
ip_address:
- "{{ ipv4_prefix + '.211' }}"
- fe80::20c:29ff:fe02:a1b3
- "{{ ipv4_prefix + '.221' }}"
- fe80::20c:29ff:fe02:a1b4
register: result
failed_when: result.changed
- name: Host absent
ipahost:
ipaadmin_password: MyPassword123
name:
- "{{ host1_fqdn }}"
- "{{ host2_fqdn }}"
- "{{ host3_fqdn }}"
update_dns: yes
state: absent