forked from Nosmoht/ansible-module-powerdns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
powerdns_record.py
265 lines (233 loc) · 9.8 KB
/
powerdns_record.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#!/usr/bin/env python
# -*- coding: utf-8 -*-
DOCUMENTATION = '''
---
module: powerdns_record
short_description: Manage PowerDNS records
description:
- Create, update or delete a PowerDNS records using API
- A record is unique identified by name and type
- Changing a records type is therefore not possible
options:
content:
description:
- Content of the record
- Could be an ip address or hostname
name:
description:
- Record name
- If name is not an FQDN, zone will be added at the end to create an FQDN
required: true
server:
description:
- Server name.
required: false
default: localhost
ttl:
description:
- Record TTL
required: false
default: 86400
type:
description:
- Record type
required: false
choices: ['A', 'AAAA', 'CNAME', 'MX', 'PTR', 'SOA', 'SRV']
default: None
zone:
description:
- Name of zone where to ensure the record
required: true
pdns_host:
description:
- Name or ip address of PowerDNS host
required: false
default: 127.0.0.1
pdns_port:
description:
- Port used by PowerDNS API
required: false
default: 8081
pdns_prot:
description:
- Protocol used by PowerDNS API
required: false
default: http
choices: ['http', 'https']
pdns_api_key:
description:
- API Key to authenticate through PowerDNS API
strict_ssl_checking:
description:
- Disables strict certificate checking
default: false
author: "Thomas Krahn (@nosmoht)"
'''
EXAMPLES = '''
- powerdns_record:
name: host01.internal.example.com
type: A
content: 192.168.1.234
state: present
zone: internal.example.com
pdns_host: powerdns.example.cm
pdns_port: 8080
pdns_prot: http
pdns_api_key: topsecret
'''
import requests
class PowerDNSError(Exception):
def __init__(self, url, status_code, message):
self.url = url
self.status_code = status_code
self.message = message
super(PowerDNSError, self).__init__()
class PowerDNSClient:
def __init__(self, host, port, prot, api_key, verify):
self.url = '{prot}://{host}:{port}/api/v1'.format(prot=prot, host=host, port=port)
self.headers = {'X-API-Key': api_key,
'content-type': 'application/json',
'accept': 'application/json'
}
self.verify = verify
def _handle_request(self, req):
if req.status_code in [200, 201, 204]:
if req.text:
try:
return json.loads(req.text)
except Exception as e:
print(e) # same as yield
return dict()
elif req.status_code == 404:
error_message = 'Not found'
else:
error_message = self._get_request_error_message(data=req)
raise PowerDNSError(url=req.url,
status_code=req.status_code,
message=error_message)
def _get_request_error_message(self, data):
request_json = data.json()
if 'error' in request_json:
request_error = request_json.get('error')
elif 'errors' in request_json:
request_error = request_json.get('errors')
else:
request_error = 'No error message found'
return request_error
def _get_zones_url(self, server):
return '{url}/servers/{server}/zones'.format(url=self.url, server=server)
def _get_zone_url(self, server, name):
return '{url}/{name}'.format(url=self._get_zones_url(server), name=name)
def get_zone(self, server, name):
req = requests.get(url=self._get_zone_url(server, name), headers=self.headers, verify=self.verify)
if req.status_code == 422: # zone does not exist
return None
return self._handle_request(req)
def get_record(self, server, zone, name):
# TODO: implementation
return dict()
def _get_request_data(self, changetype, server, zone, name, rtype, content=None, disabled=None, ttl=None):
record_content = list()
record_content.append(dict(content=content, disabled=disabled))
record = dict(name=name, type=rtype, changetype=changetype, records=record_content, ttl=ttl)
rrsets = list()
rrsets.append(record)
data = dict(rrsets=rrsets)
return data
def create_record(self, server, zone, name, rtype, content, disabled, ttl):
url = self._get_zone_url(server=server, name=zone)
data = self._get_request_data(changetype='REPLACE', server=server, zone=zone, name=name, rtype=rtype,
content=content, disabled=disabled, ttl=ttl)
req = requests.patch(url=url, data=json.dumps(data), headers=self.headers, verify=self.verify)
return self._handle_request(req)
def delete_record(self, server, zone, name, rtype):
url = self._get_zone_url(server=server, name=zone)
data = self._get_request_data(changetype='DELETE', server=server, zone=zone, name=name, rtype=rtype)
# module.fail_json(msg=json.dumps(data))
req = requests.patch(url=url, data=json.dumps(data), headers=self.headers, verify=self.verify)
return self._handle_request(req)
def ensure(module, pdns_client):
content = module.params['content']
disabled = module.params['disabled']
name = module.params['name']
rtype = module.params['type']
ttl = module.params['ttl']
zone_name = module.params['zone']
if zone_name not in name:
name = '{name}.{zone}'.format(name=name, zone=zone_name)
server = module.params['server']
state = module.params['state']
try:
zone = pdns_client.get_zone(server, zone_name)
except PowerDNSError as e:
module.fail_json(
msg='Could not get zone {name}: HTTP {code}: {err}'.format(name=zone_name, code=e.status_code,
err=e.message))
if not zone:
module.fail_json(msg='Zone not found: {name}'.format(zone=zone_name))
# Try to find the record by name and type
record = next((item for item in zone.get('rrsets') if (item['name'] == name and item['type'] == rtype)), None)
if state == 'present':
# Create record if it does not exist
if not record:
try:
pdns_client.create_record(server=server, zone=zone_name, name=name, rtype=rtype, content=content,
ttl=ttl, disabled=disabled)
return True, pdns_client.get_record(server=server, zone=zone_name, name=name)
except PowerDNSError as e:
module.fail_json(
msg='Could not create record {name}: HTTP {code}: {err}'.format(name=name, code=e.status_code,
err=e.message))
# Check if changeable parameters match, else update record.
if content not in [c.get('content') for c in record["records"]] or record.get('ttl', None) != ttl:
try:
pdns_client.create_record(server=server, zone=zone_name, name=name, rtype=rtype, content=content,
ttl=ttl, disabled=disabled)
return True, pdns_client.get_record(server=server, zone=zone_name, name=name)
except PowerDNSError as e:
module.fail_json(
msg='Could not update record {name}: HTTP {code}: {err}'.format(name=name, code=e.status_code,
err=e.message))
elif state == 'absent':
if record:
try:
pdns_client.delete_record(server=server, zone=zone_name, name=name, rtype=rtype)
return True, None
except PowerDNSError as e:
module.fail_json(
msg='Could not delete record {name}: HTTP {code}: {err}'.format(name=name, code=e.status_code,
err=e.message))
return False, record
def main():
module = AnsibleModule(
argument_spec=dict(
content=dict(type='str', required=False),
disabled=dict(type='bool', default=False),
name=dict(type='str', required=True),
server=dict(type='str', default='localhost'),
state=dict(type='str', default='present', choices=['present', 'absent']),
ttl=dict(type='int', default=86400),
type=dict(type='str', required=False, choices=['A', 'AAAA', 'CNAME', 'MX', 'PTR', 'SOA', 'SRV']),
zone=dict(type='str', required=True),
pdns_host=dict(type='str', default='127.0.0.1'),
pdns_port=dict(type='int', default=8081),
pdns_prot=dict(type='str', default='http', choices=['http', 'https']),
pdns_api_key=dict(type='str', required=False),
strict_ssl_checking=dict(type='bool', default=True),
),
supports_check_mode=True,
)
pdns_client = PowerDNSClient(host=module.params['pdns_host'],
port=module.params['pdns_port'],
prot=module.params['pdns_prot'],
api_key=module.params['pdns_api_key'],
verify=module.params['strict_ssl_checking'])
try:
changed, record = ensure(module, pdns_client)
module.exit_json(changed=changed, record=record)
except Exception as e:
module.fail_json(msg='Error: {0}'.format(str(e)))
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()