Skip to content

Commit 5fed40f

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 5fed40f

File tree

7 files changed

+220
-0
lines changed

7 files changed

+220
-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.rst

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
3+
# README for `maas_nameserver` Ansible Role
4+
5+
## Overview
6+
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 Ceph nodes and their IPMI interfaces, ensuring only desired records and domains exist while cleaning up unwanted ones. This role depends on a `secrets` role to load encrypted MAAS API credentials using Ansible Vault.
7+
8+
## Requirements
9+
- **Ansible**: Version 2.9 or higher
10+
- **MAAS CLI**: Installed on the target maas server
11+
- **Secrets Role**: A companion `secrets` role to load encrypted credentials
12+
13+
## Role Structure
14+
```
15+
roles/
16+
maas_nameserver/
17+
defaults/
18+
main.yml
19+
meta/
20+
main.yml
21+
tasks/
22+
main.yml
23+
vars/
24+
main.yml
25+
README.md
26+
```
27+
28+
## Dependencies
29+
- **`secrets` Role**: Defined in `meta/main.yml`, this role loads encrypted MAAS API credentials from a configurable path. Ensure the `secrets` role is available in your `roles/` directory.
30+
31+
## Usage
32+
1. **Place the Role**: Ensure the `maas_namesever` and `secrets` roles are in your `roles/` directory.
33+
2. **Prepare Inventory**: Define your `maas` and `ceph` groups (e.g., in `/etc/ansible/hosts/tucson`):
34+
```ini
35+
[ceph]
36+
ceph001.internal.ceph.ibm.com ip=10.18.131.1 ipmi=10.18.139.1
37+
ceph002.internal.ceph.ibm.com ip=10.18.131.2 ipmi=10.18.139.2
38+
ceph003.internal.ceph.ibm.com ip=10.18.131.3 ipmi=10.18.139.3
39+
40+
[maas]
41+
ceph-vm-14.tuc.stglabs.ibm.com ip=9.11.120.237
42+
```
43+
3. **Set Up Secrets**: Encrypt your MAAS credentials in a secrets file (see "Secrets File" section below).
44+
4. **Run the Playbook**:
45+
```bash
46+
ansible-playbook maas-dns-playbook.yml
47+
```
48+
49+
## Variables
50+
51+
### `defaults/main.yml`
52+
These are overridable defaults:
53+
- `maas_api_url`: Default MAAS API endpoint (`http://localhost:5240/MAAS/api/2.0/`). Typically overridden by the secrets file.
54+
- `maas_profile`: Default MAAS profile name (`admin`). Overridden by the secrets file.
55+
- `allowed_domains`: List of domains to preserve (default: `["maas"]`).
56+
57+
### `vars/main.yml`
58+
These are role-specific variables, not intended for override:
59+
- `dns_domains`:
60+
- `ceph`: Dynamically derived from the first Ceph host’s domain (e.g., `internal.ceph.ibm.com`).
61+
- `ipmi`: Static IPMI domain (`ipmi.ceph.ibm.com`).
62+
63+
## Secrets File
64+
The `maas_nameserver` role depends on the `secrets` role to load encrypted MAAS API credentials. The secrets file is expected at `{{ secrets_path }}/group_vars/maas.yml`, where `secrets_path` defaults to `/etc/ansible/secrets` (configurable via the `ANSIBLE_SECRETS_PATH` environment variable).
65+
66+
### Example Secrets File
67+
Create and encrypt the file (e.g., `/etc/ansible/secrets/maas.yml`):
68+
```yaml
69+
---
70+
maas_api_url: "http://X.X.X.X:5240/MAAS/api/2.0/"
71+
maas_api_key: "XXXXXXXXXXXXXXXX"
72+
maas_profile: "admin"
73+
```
74+
**Notes**:
75+
- **Location**: Store this file in a secure directory (e.g., `/etc/ansible/secrets` or a private repo like `~/secrets/ceph-secrets`). Adjust `secrets_path` if using a custom location.
76+
- **Security**: Ensure the file is readable only by the Ansible user (e.g., `chmod 600`).
77+
- **Variables**:
78+
- `maas_api_url`: The MAAS API endpoint.
79+
- `maas_api_key`: The API key for authentication.
80+
- `maas_profile`: The profile name used with the MAAS CLI.
81+
- **Vault Password**: Store the password securely (e.g., in `~/.vault_pass.txt` with `chmod 600`) or provide it at runtime.
82+
83+
## Behavior
84+
- **DNS Records**: Creates A records for Ceph nodes (`ip`) and IPMI interfaces (`ipmi`) in `internal.ceph.ibm.com` and `ipmi.ceph.ibm.com`, respectively.
85+
- **Cleanup**: Removes unwanted DNS records and domains not matching the desired state or `allowed_domains`.
86+
- **Idempotency**: Skips actions if the desired state is already met.
87+
88+
## Example Output
89+
Running the playbook should produce output similar to:
90+
```
91+
TASK [maas_nameserver : Debug desired FQDNs] ******************************************
92+
ok: [ceph-vm-14.tuc.stglabs.ibm.com] => {
93+
"msg": "Desired FQDNs: ['ceph001.internal.ceph.ibm.com', 'ceph002.internal.ceph.ibm.com', 'ceph003.internal.ceph.ibm.com', 'ceph001.ipmi.ceph.ibm.com', 'ceph002.ipmi.ceph.ibm.com', 'ceph003.ipmi.ceph.ibm.com']"
94+
}
95+
```
96+
97+
## Troubleshooting
98+
- **Secrets Not Loading**: Verify `secrets_path` points to the correct directory and the file is encrypted correctly. Check vault password access.
99+
- **MAAS CLI Errors**: Ensure the MAAS CLI is installed and the API credentials are valid.
100+
- **Inventory Issues**: Confirm that any inventory group hosts have `ip` and `ipmi` variables defined.
101+
102+
---
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
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+
allowed_domains:
5+
- "maas"

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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
- name: Ensure MAAS CLI is logged in
3+
ansible.builtin.command: "maas login {{ maas_profile }} {{ maas_api_url }} {{ maas_api_key }}"
4+
register: maas_login
5+
changed_when: maas_login.rc == 0
6+
failed_when: maas_login.rc != 0 and maas_login.stderr != "Profile already exists"
7+
8+
- name: Get existing DNS resources
9+
ansible.builtin.command: "maas {{ maas_profile }} dnsresources read"
10+
register: existing_resources
11+
changed_when: false
12+
13+
- name: Initialize DNS records list
14+
ansible.builtin.set_fact:
15+
dns_records: []
16+
17+
- name: Build DNS records for Ceph nodes
18+
ansible.builtin.set_fact:
19+
dns_records: "{{ dns_records + [{'name': host.split('.')[0], 'ip': hostvars[host]['ip'], 'type': 'A', 'domain': dns_domains.ceph}] }}"
20+
loop: "{{ groups['ceph'] }}"
21+
loop_control:
22+
loop_var: host
23+
24+
- name: Build DNS records for IPMI interfaces
25+
ansible.builtin.set_fact:
26+
dns_records: "{{ dns_records + [{'name': host.split('.')[0], 'ip': hostvars[host]['ipmi'], 'type': 'A', 'domain': dns_domains.ipmi}] }}"
27+
loop: "{{ groups['ceph'] }}"
28+
loop_control:
29+
loop_var: host
30+
when: "'ipmi' in hostvars[host]"
31+
32+
- name: Parse desired FQDNs
33+
ansible.builtin.set_fact:
34+
desired_fqdns: "{{ dns_records | map(attribute='name') | zip(dns_records | map(attribute='domain')) | map('join', '.') | list }}"
35+
36+
- name: Debug desired FQDNs
37+
ansible.builtin.debug:
38+
msg: "Desired FQDNs: {{ desired_fqdns }}"
39+
40+
- name: Remove unwanted DNS records
41+
ansible.builtin.command: "maas {{ maas_profile }} dnsresource delete {{ item.id }}"
42+
loop: "{{ existing_resources.stdout | from_json }}"
43+
when: >
44+
item.fqdn not in desired_fqdns and
45+
item.ip_addresses | map(attribute='ip') | first not in (dns_records | map(attribute='ip') | list)
46+
register: dns_deletion
47+
failed_when: dns_deletion.rc != 0 and "does not exist" not in dns_deletion.stderr
48+
49+
- name: Get updated DNS resources after deletions
50+
ansible.builtin.command: "maas {{ maas_profile }} dnsresources read"
51+
register: updated_resources
52+
changed_when: false
53+
54+
- name: Get existing DNS domains
55+
ansible.builtin.command: "maas {{ maas_profile }} domains read"
56+
register: existing_domains
57+
changed_when: false
58+
59+
- name: Parse existing domains
60+
ansible.builtin.set_fact:
61+
current_domains: "{{ existing_domains.stdout | from_json | map(attribute='name') | list }}"
62+
63+
- name: Remove unwanted domains
64+
ansible.builtin.command: "maas {{ maas_profile }} domain delete {{ item.id }}"
65+
loop: "{{ existing_domains.stdout | from_json }}"
66+
when: >
67+
item.name not in allowed_domains and
68+
item.name not in dns_domains.values() and
69+
item.resource_record_count == 0
70+
register: domain_deletion
71+
failed_when: domain_deletion.rc != 0 and "does not exist" not in domain_deletion.stderr and "protected foreign keys" not in domain_deletion.stderr
72+
73+
- name: Ensure new DNS domains exist
74+
ansible.builtin.command: "maas {{ maas_profile }} domains create name={{ item.value }}"
75+
loop: "{{ dns_domains | dict2items }}"
76+
when: item.value not in current_domains
77+
register: domain_creation
78+
failed_when: domain_creation.rc != 0 and "already exists" not in domain_creation.stderr
79+
80+
- name: Ensure DNS records exist
81+
ansible.builtin.command: >
82+
maas {{ maas_profile }} dnsresources create
83+
fqdn={{ item.name }}.{{ item.domain }}
84+
ip_addresses={{ item.ip }}
85+
loop: "{{ dns_records }}"
86+
when: >
87+
(item.name + '.' + item.domain) not in
88+
(updated_resources.stdout | from_json | map(attribute='fqdn') | list)
89+
register: dns_creation
90+
failed_when: dns_creation.rc != 0 and "already exists" not in dns_creation.stderr
91+
92+
- name: Logout from MAAS
93+
ansible.builtin.command: "maas logout {{ maas_profile }}"
94+
when: maas_login is success
95+
changed_when: true

roles/maas_nameserver/vars/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
dns_domains:
3+
ceph: "{{ (groups['ceph'] | first | split('.'))[-4:] | join('.') }}" # internal.ceph.ibm.com
4+
ipmi: "ipmi.ceph.ibm.com" # New IPMI domain

roles/secrets/tasks/main.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
- name: Include encrypted secrets for the maas nameserver role
3+
ansible.builtin.include_vars:
4+
file: "{{ secrets_path }}/maas.yml"
5+
no_log: true # Prevents sensitive data from appearing in logs

0 commit comments

Comments
 (0)