-
Notifications
You must be signed in to change notification settings - Fork 2
/
dynect
241 lines (218 loc) · 9.09 KB
/
dynect
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
#!/usr/bin/python
#
# WARNING This code is still under development
#
__author__ = 'Adham Helal'
DOCUMENTATION = '''
---
module: dynect
short_description: Add or remove dns entries in dynect
description:
- Add or remove dynect from a remote host.
version_added: "0.0.6"
options:
dependencies:
dynect_client
To pip install DynectDNS
'''
# A Class to compare between new and old records
class HostTemplate():
def __init__(self, domain=None, data=None, rtype=None, hostname=None, ttl=None):
self.domain = domain
self.data = data
self.rtype = rtype
self.hostname = hostname
self.ttl = ttl
class DynectHost():
def __init__(self, module):
self.module = module
# Create compare classes
self.real_record = HostTemplate()
self.new_record = HostTemplate()
# Assign Parms.
self.customer_name = self.module.params["customer_name"]
self.username = self.module.params["username"]
self.password = module.params["password"]
self.new_record.domain = module.params["domain"]
self.new_record.data = module.params["linkname"]
self.new_record.rtype = module.params["rtype"]
self.new_record.hostname = module.params["hostname"]
self.new_record.ttl = self.module.params["ttl"]
self.query_method = self.module.params["query_method"]
self.rest_iface = DynectRest()
self.record_id = False
self.recordInfo = ""
self.state = self.module.params["state"]
# dynect request types
self.http_req = None
self.attr_type = None
self.http_method = "POST" # Default type POST
# Do a login
self.login()
def login(self):
try:
# Log in
arguments = {'customer_name': self.customer_name, 'user_name': self.username, 'password': self.password}
response = self.rest_iface.execute('/Session/', 'POST', arguments)
if response['status'] != 'success':
self.module.fail_json(msg="Incorrect credentials")
except Exception, e:
self.module.fail_json(msg="Connection error : %s" % e)
def dynect_publish(self):
args = {'publish': 'True'}
try:
self.recordInfo = self.rest_iface.execute('/Zone/' + self.new_record.domain, 'PUT', args)
except Exception, e:
self.module.fail_json(msg="Failed to publish domain : %s" % e)
if self.recordInfo['status'] == 'success':
return True
else:
return False
def dynect_pre_request(self):
if self.new_record.rtype == "A":
self.http_req = "ARecord"
self.attr_type = "address"
else:
self.http_req = "CNAMERecord"
self.attr_type = "cname"
def dynect_get_record(self):
# TODO: Will fail if record has mutli fields i.e. 2 mx records or 3 A with round robin
if not self.record_id:
self.dynect_pre_request()
try:
self.recordInfo = self.rest_iface.execute('/' + self.http_req + '/' + self.new_record.domain + '/' +
self.new_record.hostname, 'GET')
except Exception, e:
self.module.fail_json(msg="Failed to get dynect record: %s" % e)
if self.recordInfo['status'] != 'success':
self.module.fail_json(msg="Failed to get dynect record : %s" % self.recordInfo)
#TODO: Might be inc
self.record_id = self.recordInfo["data"]
def dynect_create_update_record(self, req_type="POST"):
self.dynect_pre_request()
args = {'ttl': self.new_record.ttl, 'rdata': {self.attr_type: self.new_record.data}}
if req_type == "POST":
req_string = '/' + self.http_req + '/' + self.new_record.domain + '/' + self.new_record.hostname
elif req_type == "PUT":
if not self.record_id:
# Will fail if record is different type. i.e. original record is A and updating to CNAME
self.module.fail_json(msg="Request returned an empty JSON record ID. Record Info='%s" % self.recordInfo)
else:
req_string = self.record_id[0]
try:
self.recordInfo = self.rest_iface.execute(req_string, req_type, args)
except Exception, e:
self.module.fail_json(msg="Failed to create/update dynect record request: %s, type:%s, Error: %s" %
(req_string, req_type, e))
if self.recordInfo['status'] == 'success':
return self.dynect_publish()
else:
self.module.fail_json(msg="Status is not success. Record info='%s'" % self.recordInfo)
def dynect_delete_record(self):
self.dynect_get_record()
try:
self.recordInfo = self.rest_iface.execute(self.record_id[0], 'DELETE')
except Exception, e:
self.module.fail_json(msg="Failed to Delete dynect record: %s" % e)
if self.recordInfo['status'] == 'success':
return self.dynect_publish()
else:
return False
def dynect_logout(self):
try:
# Logout
self.rest_iface.execute('/Session/', 'DELETE')
except Exception, e:
self.recordInfo += " Failed to logout: %s" % e
def record_match(self):
condition = True
if self.query_method == "API":
condition &= self.new_record.domain == self.real_record.domain
condition &= self.new_record.ttl == self.real_record.ttl
condition &= self.new_record.data == self.real_record.data
condition &= self.new_record.rtype == self.real_record.rtype
condition &= self.new_record.hostname == self.real_record.hostname
# dig match only compare hostname/IP
condition &= self.new_record.data == self.real_record.data
# condition &= self.new_record.hostname == self.real_record.hostname
return condition
def record_exist(self):
if self.query_method == "API":
# TODO: Use dynect API to resolve or not API calls are expensive
self.module.fail_json(msg="API CHECK NOT YET WORKING")
else:
# Use dig to resolve
import subprocess
dns_server = "@ns1.p22.dynect.net"
p = subprocess.Popen(["dig", "+short", self.new_record.hostname, dns_server], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=False)
(output, err) = p.communicate()
if p.returncode == 0:
if output:
output = output.partition("\n")[0]
if output[-1] == ".":
output = output[:-1]
self.real_record.data = output
# Record exist
self.recordInfo = "dig: Record exists output : " + output
return True
else:
self.module.fail_json(msg="unable to use dig command to resolve: %s" % self.new_record.hostname)
# No match record does not exists
return False
def host_create(self):
req_type = "POST"
if self.record_exist():
if self.record_match(): # no need to change anything
return False
else: # Get record_id and Update record
self.dynect_get_record()
self.http_method = "PUT"
req_type = "PUT"
if self.module.check_mode:
return True
else:
# Create/update record
return self.dynect_create_update_record(req_type)
def host_delete(self):
if self.record_exist():
if self.module.check_mode:
return True
else:
return self.dynect_delete_record()
else:
return False
def main(self):
if self.state == "present":
changed = self.host_create()
else:
changed = self.host_delete()
self.module.exit_json(changed=changed, msg=self.recordInfo)
def main():
module = AnsibleModule(
argument_spec=dict(
customer_name=dict(required=True),
username=dict(required=True),
password=dict(required=True),
domain=dict(required=True),
state=dict(default="present", choices=["absent", "present"]),
linkname=dict(required=True),
rtype=dict(default="A", choices=["A", "CNAME"]),
hostname=dict(default=""),
query_method=dict(default="dig", choices=["dig", "api"]),
ttl=dict(default=3600, type="int"),
),
supports_check_mode=True
)
if not dynect_client_found:
module.fail_json(msg="The ansible dynect module requires DynectDNS library. use 'pip install DynectDNS' ")
DynectHost(module).main()
try:
from dynect.DynectDNS import DynectRest
except ImportError:
dynect_client_found = False
else:
dynect_client_found = True
# import module snippets
from ansible.module_utils.basic import *
main()