Batch the creation of dns entries

This should reduce the time spent creating dns entries by
doing most of the pre-processing in a python module, and reducing
the number of calls made to IPA.

Also modified the molecule tests to allow for cases where more than
one IP record is returned.  In this case, the regex was too
restrictive.

Also modified the reverse DNS test to handle the ipv6 case
correctly.  It looks like we were creating zones incorrectly for
ipv6 before.

Also added some code to the end of the prepare.yml for the deregister
test, to make sure the ipa server is really up before continuing.

Change-Id: If88fc20c52e8ff4af77efaef920dd3de62723d0f
This commit is contained in:
Ade Lee 2023-03-20 20:26:50 +00:00 committed by Grzegorz Grasza
parent f54ffe985b
commit efdc9022e3
6 changed files with 180 additions and 131 deletions

View File

@ -0,0 +1,107 @@
#!/usr/bin/python
#
# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ipaddress import ip_address, IPv4Address
def _parse_host_entry(entry):
if len(entry.split()) >= 2:
value = entry.split()[0]
try:
ip = ip_address(value)
record_type = 'A' if (type(ip) is IPv4Address) else 'AAAA'
except ValueError:
return "", "", "Invalid", ""
host = entry.split()[1]
host_split = host.split('.', 1)
if (len(host_split) == 2):
name = host_split[0]
zone = host_split[1]
return value, name, record_type, zone
return "", "", "Invalid", ""
def _parse_hosts_entries(hosts_entries, cdomain):
"""Extract parameters to add dns entries from hosts_data
---
:param hosts_data: is a list of strings, with each string being a line
in the /etc/hosts file.
:returns: two lists. a list of dicts containing the parameters needed for
ansible-freeipa dnsrecord to add, and a list of dicts containing
dnszones to add.
"""
zones = set()
records = list()
ips = set()
for host_entry in hosts_entries:
value, name, record_type, zone = _parse_host_entry(host_entry)
print("cdomain is " + cdomain)
if record_type == "Invalid":
continue
if not zone.endswith(cdomain):
print("zone " + zone + " not added")
continue
# add forward zone
if zone not in zones:
print("adding " + zone)
zones.add(zone)
# add ips to get reverse zones
if value not in ips:
ips.add(value)
# add forward record
if (record_type == "A"):
record = {"record_name": name,
"zone_name": zone,
"record_type": record_type,
"create_reverse": "true",
"a_rec": value}
else:
record = {"record_name": name,
"zone_name": zone,
"record_type": record_type,
"create_reverse": "true",
"aaaa_rec": value}
records.append(record)
return [list(zones), list(ips), records]
def get_dns_zones(hosts_entries, cdomain):
return _parse_hosts_entries(hosts_entries, cdomain)[0]
def get_ips(hosts_entries, cdomain):
return _parse_hosts_entries(hosts_entries, cdomain)[1]
def get_dns_entries(hosts_entries, cdomain):
return _parse_hosts_entries(hosts_entries, cdomain)[2]
class FilterModule(object):
def filters(self):
return {
'get_dns_entries': get_dns_entries,
'get_ips': get_ips,
'get_dns_zones': get_dns_zones}

View File

@ -197,7 +197,7 @@ def test_dns(host, ip, name):
result = host.check_output(
'ipa dnsrecord-find {} --name={}'.format(
zone_name, record_name))
assert 'record: {}'.format(ip) in result
assert ip in result
@pytest.mark.parametrize('ip, name', [
@ -234,12 +234,15 @@ def test_dns_absent(host, ip, name):
('172.16.0.195', 'overcloud-novacompute-0.tenant'),
('192.168.24.128', 'overcloud-novacompute-0.ctlplane')])
def test_reverse_dns(host, ip, name):
reverse = ipaddress.ip_address(ip).reverse_pointer
record, zone = reverse.split('.', 1)
split_char = '.'
addr = ipaddress.ip_address(ip)
k = 1 if (addr.version == 4) else 16
temp = addr.reverse_pointer.split(split_char)
record, zone = split_char.join(temp[:k]), split_char.join(temp[k:])
result = host.check_output(
'ipa dnsrecord-find {} --name={}'.format(
zone, record))
assert 'record: {}'.format(name) in result
assert name in result
@pytest.mark.parametrize('ip, name', [

View File

@ -74,3 +74,22 @@
search_regex: "(INFO The ipa-server-install command was successful|ERROR The ipa-server-install command failed)"
timeout: 900
become: true
- name: Wait for FreeIPA LDAP port to open
wait_for:
host=10.88.0.22
port=389
delay=1
timeout=300
ignore_errors: true
- name: Check the status of ipactl to make sure all services are started
command: "sudo podman exec freeipa-server-container ipactl status"
retries: 10
delay: 3
register: result
until: result.rc == 0
- name: Print ipactl status
debug:
msg: "{{ result.stdout }}"

View File

@ -1,123 +0,0 @@
---
# Copyright 2020 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
- name: set forward dns record values
set_fact:
record_value: "{{ item.split()[0] }}"
record_name: "{{ item.split()[1].split('.', 1)[0] }}"
zone_name: "{{ item.split()[1].split('.', 1)[1] }}"
when: item.split() | length >= 2 and item.split()[1].split('.') | length >= 2
- name: set alternative record values
set_fact:
record_value: "no record value"
record_name: "no record name"
zone_name: "no record zone name provided"
when: item.split() | length < 2 or item.split()[1].split('.') | length < 2
- name: Notify about not adding entries
debug:
msg: |
"{{ item }}" not added to DNS due to not being managed by us.
Entries with domains outside of cloud_domain are skipped.
when: not zone_name is match("^(|.+\.)" + cloud_domain + "$")
- name: add entries
block:
- name: set record type
set_fact:
record_type: "{{ 'A' if record_value| ansible.netcommon.ipv4 else 'AAAA' }}"
- name: add dns zone
ipadnszone:
name: "{{ zone_name }}"
become: true
- name: Modify or add forward dns
block:
- name: try modifying forward dns record
ipadnsrecord:
zone_name: "{{ zone_name }}"
record_name: "{{ record_name }}"
record_type: "{{ record_type }}"
a_rec: "{{ record_value }}"
a_ip_address: ""
when: record_type == 'A'
become: true
- name: try modifying forward dns record
ipadnsrecord:
zone_name: "{{ zone_name }}"
record_name: "{{ record_name }}"
record_type: "{{ record_type }}"
aaaa_rec: "{{ record_value }}"
aaaa_ip_address: ""
when: record_type == 'AAAA'
become: true
rescue:
- name: add forward dns record
ipadnsrecord:
zone_name: "{{ zone_name }}"
record_name: "{{ record_name }}"
record_type: "{{ record_type }}"
record_value: "{{ record_value }}"
become: true
- name: get reverse record data
set_fact:
reverse_addr: "{{ record_value | ipaddr('revdns') }}"
- name: set reverse record entries for ipv4
set_fact:
reverse_record_zone: "{{ reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv4|int)[-1] }}"
reverse_record_name: "{{ '.'.join(reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv4|int)[:-1]) }}"
when: record_type == 'A'
- name: set reverse record entries for ipv6
set_fact:
reverse_record_zone: "{{ reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv6|int)[-1] }}"
reverse_record_name: "{{ '.'.join(reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv6|int)[:-1]) }}"
when: record_type == 'AAAA'
- name: add reverse record dns zone
ipadnszone:
name: "{{ reverse_record_zone }}"
register: reverse_zone_result
failed_when: reverse_zone_result.failed and 'already exists in DNS' not in reverse_zone_result.msg
become: true
- name: Modify or add reverse dns record
block:
- name: try modifying reverse dns record
ipadnsrecord:
zone_name: "{{ reverse_record_zone }}"
record_name: "{{ reverse_record_name }}"
record_type: "PTR"
ptr_rec: "{{ record_name }}.{{ zone_name }}."
ptr_hostname: ""
become: true
rescue:
- name: add reverse dns record
ipadnsrecord:
zone_name: "{{ reverse_record_zone }}"
record_name: "{{ reverse_record_name }}"
record_type: "PTR"
record_value: "{{ record_name }}.{{ zone_name }}."
register: reverse_record_result
failed_when: reverse_zone_result.failed and 'already exists in DNS' not in reverse_zone_result.msg
become: true
when: zone_name is match("^(|.+\.)" + cloud_domain + "$")

View File

@ -20,7 +20,40 @@
# - host_entry (host entries string, in a format similar to /etc/hosts)
- name: add dns records
include_tasks:
file: dns.yaml
loop: "{{ hosts_entry }}"
- name: get dns zones and entries to add
set_fact:
dns_zones: "{{ hosts_entry | get_dns_zones(cdomain=cloud_domain) }}"
ips_for_reverse: "{{ hosts_entry | get_ips(cdomain=cloud_domain) }}"
dns_entries: "{{ hosts_entry | get_dns_entries(cdomain=cloud_domain) }}"
no_reverse_ips: []
- name: add dns zones
loop: "{{ dns_zones }}"
ipadnszone:
name: "{{ item }}"
become: true
- name: add reverse dns zones fixing errors
include_tasks: reverse_zones.yml
vars:
ip: "{{ item }}"
loop: "{{ ips_for_reverse }}"
- name: set create_reverse to false for elements of no_reverse_ips
set_fact:
u_dns_entries: "{{ u_dns_entries |d([]) + [item|combine({'create_reverse': _rev|from_yaml})] }}"
loop: "{{ dns_entries }}"
vars:
_rev: |
{% if (item.record_type == 'A') and (item.a_rec in no_reverse_ips) %}
False
{% elif (item.record_type == 'AAAA') and (item.aaaa_rec in no_reverse_ips) %}
False
{% else %}
True
{% endif %}
- name: add dns forward and reverse records
ipadnsrecord:
records: "{{ u_dns_entries }}"
become: true

View File

@ -0,0 +1,10 @@
- name: block to add reverse zone
block:
- name: add reverse zone
ipadnszone:
name_from_ip: "{{ ip }}"
become: true
rescue:
- name: add ip to no_reverse_ips
set_fact:
no_reverse_ips: "{{ no_reverse_ips + [ip] }}"