Skip to content

Commit f3d88ad

Browse files
author
Adam Kraitman
committed
maas dns role for configuring maas dns domains and records
Signed-off-by: Adam Kraitman <akraitma@li-8b09b2cc-35b7-11b2-a85c-cd1dbade58f9.ibm.com>
1 parent fcd39aa commit f3d88ad

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

maas_nameserver.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
- name: Configure MAAS DNS
3+
hosts: maas
4+
gather_facts: false
5+
roles:
6+
- maas_nameserver

roles/maas_nameserver/README.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
README for maas_nameserver Ansible Role
2+
Overview
3+
The maas_nameserver role configures DNS domains and records in MAAS (Metal as a Service) based on an Ansible inventory. It manages DNS entries for hosts (e.g., Main interfaces, IPMI interfaces, VLAN interfaces) in specified domains, ensuring only desired records and domains exist while cleaning up unwanted ones. This role depends on a secrets file to load MAAS API credentials.
4+
Requirements
5+
6+
Ansible: Version 2.9 or higher
7+
MAAS CLI: Installed on the target MAAS server
8+
Inventory: A valid Ansible inventory with group_vars/all.yml defining dns_domains
9+
10+
Role Structure
11+
roles/
12+
maas_nameserver/
13+
defaults/
14+
main.yml
15+
handlers/
16+
main.yml
17+
tasks/
18+
main.yml
19+
vars/
20+
main.yml
21+
README.md
22+
23+
Dependencies
24+
25+
Secrets File: A maas.yml file at {{ secrets_path }}/maas.yml provides MAAS API credentials (e.g., maas_api_key, maas_api_url). No separate secrets role is required; credentials are loaded via include_vars.
26+
27+
Usage
28+
29+
Place the Role: Ensure the maas_nameserver role is in your roles/ directory.
30+
31+
Prepare Inventory: Define your maas and target host groups:
32+
[maas]
33+
maas.internal.ceph.ibm.com ip=10.11.120.237
34+
35+
[snipe]
36+
snipe.internal.ceph.ibm.com ip=10.60.100.11
37+
38+
[machine]
39+
machine001.internal.ceph.ibm.com ip=10.18.131.100 ipmi=10.18.139.100 vlan104=10.18.144.3
40+
41+
42+
Set Up Inventory Variables: Define dns_domains in group_vars/all.yml:
43+
---
44+
dns_domains:
45+
ceph: "internal.ceph.ibm.com"
46+
ipmi: "ipmi.ceph.ibm.com"
47+
vlan104: "vlan104.internal.ceph.ibm.com"
48+
49+
50+
Set Up Secrets: Create a secrets file at {{ secrets_path }}/maas.yml:
51+
---
52+
maas_api_key: "XXXXXXXXXXXXXXXX"
53+
maas_api_url: "http://localhost:5240/MAAS/api/2.0/"
54+
maas_profile: "admin"
55+
56+
57+
Run the Playbook:
58+
ansible-playbook maas_nameserver.yml
59+
60+
61+
62+
Variables
63+
defaults/main.yml
64+
These are overridable defaults:
65+
66+
maas_api_url: Default MAAS API endpoint (http://localhost:5240/MAAS/api/2.0/). Override in secrets/maas.yml.
67+
68+
maas_profile: Default MAAS profile name (admin). Override in secrets/maas.yml.
69+
70+
default_domains: Domains to preserve (default: ["maas"]). The maas domain is used by MAAS for internal DNS records and is excluded from cleanup.
71+
72+
target_hosts: List of hosts for DNS records. Defaults to all inventory hosts except the maas group (e.g., ['machine001.internal.ceph.ibm.com', 'snipe.internal.ceph.ibm.com']). Override in secrets/maas.yml or via extra vars:
73+
target_hosts:
74+
- machine001.internal.ceph.ibm.com
75+
- machine002.internal.ceph.ibm.com
76+
77+
Or via command line:
78+
ansible-playbook maas_nameserver.yml -e "target_hosts=['argo001.internal.ceph.ibm.com']"
79+
80+
81+
82+
secrets/maas.yml
83+
Provides MAAS API credentials and optional overrides:
84+
85+
maas_api_key: MAAS API key for authentication.
86+
maas_api_url: MAAS API endpoint (e.g., http://127.0.0.1:5240/MAAS/api/2.0/).
87+
maas_profile: MAAS CLI profile name (e.g., admin).
88+
target_hosts (optional): Override the default target_hosts list.
89+
90+
Example:
91+
maas_api_key: "XXXXXXXXXXXXXXXX"
92+
maas_api_url: "http://127.0.0.1:5240/MAAS/api/2.0/"
93+
maas_profile: "admin"
94+
# Optional override
95+
target_hosts:
96+
- machine001.internal.ceph.ibm.com
97+
98+
Notes:
99+
100+
Security: Ensure file permissions are restricted (e.g., chmod 600 maas.yml).
101+
Vault: If encrypted, provide the vault password (e.g., via --vault-password-file ~/.vault_pass.txt).
102+
103+
Behavior
104+
105+
DNS Records: Creates A records for hosts based on dns_domains and inventory variables (e.g., ip, ipmi, vlan104). Example:
106+
machine001.internal.ceph.ibm.com → 10.18.131.100
107+
machine001.ipmi.ceph.ibm.com → 10.18.139.100
108+
machine001.vlan104.internal.ceph.ibm.com → 10.18.144.3
109+
110+
111+
Cleanup: Deletes DNS records and domains not in dns_domains or default_domains.
112+
Error Handling: Logs failed DNS record deletions via a handler (log_deletion_failure).
113+
Idempotency: Skips actions if the desired state is already met.
114+
115+
Troubleshooting
116+
117+
Missing dns_domains: Ensure dns_domains is defined in inventory/group_vars/all.yml. The playbook will fail if undefined.
118+
Secrets Not Loading: Verify secrets_path points to the correct directory and maas.yml contains valid credentials.
119+
MAAS CLI Errors: Confirm the MAAS CLI is installed on the target server and the API key is valid.
120+
Failed Deletions: Check handler output for log_deletion_failure messages indicating why DNS record deletions failed (e.g., permissions, network issues).
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
maas_api_url: "http://localhost:5240/MAAS/api/2.0/" # Default, overridden by secrets
3+
maas_profile: "admin" # Default, overridden by secrets
4+
default_domains:
5+
- "maas"
6+
7+
# List of target hosts for DNS records, excluding the 'maas' group by default.
8+
# Can be overridden in secrets/maas.yml (e.g., target_hosts: ['host1.example.com', 'host2.example.com'])
9+
# or via extra vars (e.g., ansible-playbook -e "target_hosts=['host1.example.com']").
10+
# If not set, defaults to all hosts in inventory groups except 'maas'.
11+
target_hosts: []
12+
13+
# Note: dns_domains should be defined in the inventory's group_vars/all.yml

roles/maas_nameserver/meta/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
dependencies:
3+
- role: secrets

roles/maas_nameserver/tasks/main.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
# Include secrets from maas.yml (e.g., maas_api_key, maas_api_url).
3+
# Note: dns_domains should be defined in the inventory's group_vars/all.yml
4+
- name: Include secrets
5+
ansible.builtin.include_vars: "{{ item }}"
6+
no_log: true
7+
with_first_found:
8+
- "{{ secrets_path | mandatory }}/maas.yml"
9+
tags:
10+
- always
11+
12+
- name: Ensure MAAS CLI is logged in
13+
ansible.builtin.command: "maas login {{ maas_profile }} {{ maas_api_url }} {{ maas_api_key }}"
14+
register: maas_login
15+
changed_when: maas_login.rc == 0
16+
failed_when: maas_login.rc != 0 and maas_login.stderr != "Profile already exists"
17+
18+
- name: Get existing DNS resources
19+
ansible.builtin.command: "maas {{ maas_profile }} dnsresources read"
20+
register: existing_resources
21+
changed_when: false
22+
23+
- name: Initialize DNS records list
24+
ansible.builtin.set_fact:
25+
dns_records: []
26+
27+
# Define target hosts for DNS records, excluding the 'maas' group.
28+
# After population, target_hosts will contain all hosts from inventory groups except 'maas', e.g.,
29+
# ['machine001.internal.example.com', 'snipe.internal.example.com', 'vm-14.example.com'].
30+
# This can be overridden in secrets/maas.yml (e.g., target_hosts: ['host1.example.com', 'host2.example.com'])
31+
# or via extra vars (e.g., -e "target_hosts=['host1.example.com']").
32+
- name: Define target hosts for DNS records
33+
ansible.builtin.set_fact:
34+
target_hosts: "{{ target_hosts | default(groups | dict2items | rejectattr('key', 'equalto', 'maas') | map(attribute='value') | flatten | unique | default([])) }}"
35+
when: groups.keys() | length > 1
36+
37+
- name: Build DNS records for all interfaces
38+
ansible.builtin.set_fact:
39+
dns_records: "{{ dns_records + [{'name': item[0].split('.')[0], 'ip': interface_ip, 'type': 'A', 'domain': item[1].value}] }}"
40+
loop: "{{ (target_hosts | default([])) | product(dns_domains | dict2items) | list }}"
41+
vars:
42+
interface_ip: "{{ hostvars[item[0]][item[1].key] if item[1].key != 'ceph' else hostvars[item[0]]['ip'] }}"
43+
when:
44+
- target_hosts is defined and target_hosts | length > 0
45+
- "item[1].key in hostvars[item[0]] or (item[1].key == 'ceph' and 'ip' in hostvars[item[0]])"
46+
47+
- name: Parse desired FQDNs
48+
ansible.builtin.set_fact:
49+
desired_fqdns: "{{ dns_records | map(attribute='name') | zip(dns_records | map(attribute='domain')) | map('join', '.') | list }}"
50+
when: dns_records | length > 0
51+
52+
- name: Remove unwanted DNS records
53+
ansible.builtin.command: "maas {{ maas_profile }} dnsresource delete {{ item.id }}"
54+
loop: "{{ existing_resources.stdout | from_json }}"
55+
when: >
56+
dns_records | length > 0 and
57+
item.fqdn not in desired_fqdns
58+
register: dns_deletion
59+
failed_when: dns_deletion.rc != 0 and "does not exist" not in dns_deletion.stderr
60+
notify: log_deletion_failure
61+
62+
- name: Get updated DNS resources after deletions
63+
ansible.builtin.command: "maas {{ maas_profile }} dnsresources read"
64+
register: updated_resources
65+
changed_when: false
66+
67+
- name: Get existing DNS domains
68+
ansible.builtin.command: "maas {{ maas_profile }} domains read"
69+
register: existing_domains
70+
changed_when: false
71+
72+
- name: Parse existing domains
73+
ansible.builtin.set_fact:
74+
current_domains: "{{ existing_domains.stdout | from_json | map(attribute='name') | list }}"
75+
76+
- name: Remove unwanted domains
77+
ansible.builtin.command: "maas {{ maas_profile }} domain delete {{ item.id }}"
78+
loop: "{{ existing_domains.stdout | from_json }}"
79+
when: >
80+
item.name not in default_domains and
81+
item.name not in dns_domains.values()
82+
register: domain_deletion
83+
failed_when: domain_deletion.rc != 0 and "does not exist" not in domain_deletion.stderr and "protected foreign keys" not in domain_deletion.stderr
84+
85+
- name: Ensure new DNS domains exist
86+
ansible.builtin.command: "maas {{ maas_profile }} domains create name={{ item.value }}"
87+
loop: "{{ dns_domains | dict2items }}"
88+
when: item.value not in current_domains
89+
register: domain_creation
90+
failed_when: domain_creation.rc != 0 and "already exists" not in domain_creation.stderr
91+
92+
- name: Ensure DNS records exist
93+
ansible.builtin.command: >
94+
maas {{ maas_profile }} dnsresources create
95+
fqdn={{ item.name }}.{{ item.domain }}
96+
ip_addresses={{ item.ip }}
97+
loop: "{{ dns_records }}"
98+
when: >
99+
dns_records | length > 0 and
100+
(item.name + '.' + item.domain) not in
101+
(updated_resources.stdout | from_json | map(attribute='fqdn') | list)
102+
register: dns_creation
103+
failed_when: dns_creation.rc != 0 and "already exists" not in dns_creation.stderr
104+
105+
- name: Logout from MAAS
106+
ansible.builtin.command: "maas logout {{ maas_profile }}"
107+
when: maas_login is success
108+
changed_when: true

roles/maas_nameserver/vars/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
---

0 commit comments

Comments
 (0)