Last active
December 21, 2015 01:59
-
-
Save Excedrin/6232221 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# This file is part of Ansible | |
# | |
# Ansible is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# Ansible is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. | |
DOCUMENTATION = ''' | |
--- | |
module: bind | |
version_added: "1.3" | |
short_description: add, update or delete entries in BIND zone file | |
description: | |
- Creates and deletes DNS records in BIND zone file | |
options: | |
command: | |
description: | |
- Specifies the action to take. | |
required: true | |
default: null | |
aliases: [] | |
choices: [ 'get', 'create', 'delete' ] | |
path: | |
description: | |
- Path to zone file | |
required: true | |
default: null | |
aliases: [] | |
zone: | |
description: | |
- The DNS zone to modify | |
required: true | |
default: null | |
aliases: [] | |
record: | |
description: | |
- The full DNS record to create or delete | |
required: true | |
default: null | |
aliases: [] | |
ttl: | |
description: | |
- The TTL to give the new record, TTL won't be reset to the default when a record is being changed and TTL isn't specified | |
required: false | |
default: 3600 (one hour) | |
aliases: [] | |
type: | |
description: | |
- The type of DNS record to create | |
required: true | |
default: null | |
aliases: [] | |
choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS' ] | |
value: | |
description: | |
- The new value when creating a DNS record. Multiple comma-spaced values are allowed. | |
required: false | |
default: null | |
aliases: [] | |
requirements: [ "dnspython" ] | |
author: Scott Cruzen | |
''' | |
EXAMPLES = ''' | |
# Add new.foo.com as an A record with 3 IPs | |
- bind: > | |
command=create | |
path=/var/named/foo.com.zone | |
zone=foo.com | |
record=new.foo.com | |
type=A | |
ttl=7200 | |
value=1.1.1.1,2.2.2.2,3.3.3.3 | |
# Retrieve the details for new.foo.com | |
- bind: > | |
command=get | |
path=/var/named/foo.com.zone | |
zone=foo.com | |
record=new.foo.com | |
type=A | |
register: rec | |
# Delete new.foo.com A record using the results from the get command | |
- bind: > | |
command=delete | |
path=/var/named/foo.com.zone | |
zone=foo.com | |
record={{ r.set.record }} | |
type={{ r.set.type }} | |
value={{ r.set.value }} | |
# Add an AAAA record. Note that because there are colons in the value | |
# that the entire parameter list must be quoted: | |
- bind: > | |
"command=create | |
path=/var/named/foo.com.zone | |
zone=foo.com | |
record=localhost.foo.com | |
type=AAAA | |
ttl=7200 | |
value=::1" | |
''' | |
import sys | |
import time | |
try: | |
import dns.zone | |
except ImportError: | |
print "failed=True msg='dnspython required for this module'" | |
sys.exit(1) | |
def main(): | |
argument_spec = dict( | |
command = dict(choices=['get', 'create', 'delete'], required=True), | |
zone = dict(required=True), | |
path = dict(required=True), | |
record = dict(required=True), | |
ttl = dict(required=False, default=-1), | |
type = dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS'], required=True), | |
value = dict(required=False), | |
) | |
argument_spec['class'] = dict(choices=['ANY','IN'], required=False, default='IN') | |
module = AnsibleModule(argument_spec) | |
command_in = module.params.get('command') | |
zone_in = module.params.get('zone') | |
path_in = module.params.get('path') | |
ttl_in = module.params.get('ttl') | |
record_in = module.params.get('record') | |
type_in = module.params.get('type') | |
class_in = module.params.get('class') | |
value_in = module.params.get('value') | |
value_list = () | |
if type(value_in) is str: | |
if value_in: | |
value_list = sorted(value_in.split(',')) | |
elif type(value_in) is list: | |
value_list = sorted(value_in) | |
if zone_in[-1:] != '.': | |
zone_in += "." | |
if record_in[-1:] != '.': | |
record_in += "." | |
typeval = dns.rdatatype._by_text.get(type_in, None) | |
if typeval is None: | |
raise NameError("Invalid type: %s" %type_in) | |
classval = dns.rdataclass._by_text.get(class_in, None) | |
if classval is None: | |
raise NameError("Invalid class: %s" %class_in) | |
default_ttl = False | |
ttl_int = long(ttl_in) | |
if ttl_int == -1: | |
default_ttl = True | |
ttl_int = 3600 | |
if command_in == 'create': | |
if not value_in: | |
module.fail_json(msg = "parameter 'value' required for create/delete") | |
try: | |
zone = dns.zone.from_file(path_in, zone_in, relativize=False) | |
except IOError, e: | |
module.fail_json(msg = e.strerror) | |
record = {} | |
name = dns.name.from_text(record_in) | |
found_record = False | |
if zone.nodes.has_key(name): | |
node = zone.nodes[name] | |
rdata = node.get_rdataset(classval, typeval) | |
if rdata: | |
records = map(lambda x: x.to_text(), sorted(rdata.items)) | |
found_record = True | |
record['zone'] = zone_in | |
record['type'] = type_in | |
record['record'] = record_in | |
record['ttl'] = rdata.ttl | |
record['value'] = ','.join(records) | |
record['values'] = records | |
if command_in == 'create' and value_list == records and ttl_int == rdata.ttl: | |
module.exit_json(changed=False) | |
if command_in == 'get': | |
module.exit_json(changed=False, set=record) | |
if command_in == 'delete' and not found_record: | |
module.exit_json(changed=False) | |
if command_in == 'create': | |
node = zone.get_node(name, create=True) | |
ttl = ttl_int | |
if default_ttl: | |
currentrdataset = node.get_rdataset(classval, typeval) | |
if currentrdataset: | |
ttl = currentrdataset.ttl | |
newrdset = dns.rdataset.from_text_list(class_in, type_in, ttl, value_list) | |
node.replace_rdataset(newrdset) | |
if command_in == 'delete': | |
zone.delete_node(name) | |
# new serial, from easyzone | |
soa = zone[zone.origin].get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA).items[0] | |
new_serial = int(time.strftime('%Y%m%d00', time.localtime(time.time()))) | |
if new_serial <= soa.serial: | |
new_serial = soa.serial + 1 | |
soa.serial = new_serial | |
try: | |
tmp = path_in + ".tmp" | |
# this generally protects against coding errors in the above | |
# that only show up when one (new or modified) record is broken | |
zone.to_file(tmp, relativize=False) | |
os.rename(tmp, path_in) | |
except IOError, e: | |
module.fail_json(msg = e.strerror) | |
except TypeError, e: | |
# these are not particularly useful | |
module.fail_json(msg = e.message) | |
module.exit_json(changed=True) | |
# this is magic, see lib/ansible/module_common.py | |
#<<INCLUDE_ANSIBLE_MODULE_COMMON>> | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You forgot to document the 'path' parameter.