summaryrefslogtreecommitdiffstats
path: root/roles/lib_dyn
diff options
context:
space:
mode:
Diffstat (limited to 'roles/lib_dyn')
-rw-r--r--roles/lib_dyn/library/dyn_record.py164
1 files changed, 112 insertions, 52 deletions
diff --git a/roles/lib_dyn/library/dyn_record.py b/roles/lib_dyn/library/dyn_record.py
index 7b80064f4..42d970060 100644
--- a/roles/lib_dyn/library/dyn_record.py
+++ b/roles/lib_dyn/library/dyn_record.py
@@ -13,6 +13,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+# pylint: disable=too-many-branches
'''Ansible module to manage records in the Dyn Managed DNS service'''
DOCUMENTATION = '''
---
@@ -84,9 +85,18 @@ options:
description:
- 'Record's "Time to live". Number of seconds the record remains cached'
- 'in DNS servers or c(0) to use the default TTL for the zone.'
+ - 'This option is mutually exclusive with use_zone_ttl'
required: false
default: 0
+ use_zone_ttl:
+ description:
+ - 'Use the DYN Zone's Default TTL'
+ - 'This option is mutually exclusive with record_ttl'
+ required: false
+ default: false
+ mutually exclusive with: record_ttl
+
notes:
- The module makes a broad assumption that there will be only one record per "node" (FQDN).
- This module returns record(s) in the "result" element when 'state' is set to 'present'. This value can be be registered and used in your playbooks.
@@ -96,18 +106,28 @@ author: "Russell Harrison"
'''
EXAMPLES = '''
+# Attempting to cname www.example.com to web1.example.com
+- name: Update CNAME record
+ dyn_record:
+ state: present
+ record_fqdn: www.example.com
+ zone: example.com
+ record_type: CNAME
+ record_value: web1.example.com
+ record_ttl: 7200
+
+# Use the zones default TTL
- name: Update CNAME record
- local_action:
- module: dyn_record
+ dyn_record:
state: present
record_fqdn: www.example.com
zone: example.com
record_type: CNAME
record_value: web1.example.com
+ use_zone_ttl: true
- name: Update A record
- local_action:
- module: dyn_record
+ dyn_record:
state: present
record_fqdn: web1.example.com
zone: example.com
@@ -144,7 +164,10 @@ def get_record_type(record_key):
return record_key.replace('_records', '').upper()
def get_record_key(record_type):
- '''Get the key to look up records in the dictionary returned from get_any_records.'''
+ '''Get the key to look up records in the dictionary returned from get_any_records.
+ example:
+ 'cname_records'
+ '''
return record_type.lower() + '_records'
def get_any_records(module, node):
@@ -166,14 +189,41 @@ def get_any_records(module, node):
def get_record_values(records):
'''Get the record values for each record returned by get_any_records.'''
- # This simply returns the values from a dictionary of record objects
+ # This simply returns the values from a record
ret_dict = {}
for key in records.keys():
record_type = get_record_type(key)
- record_value_param = RECORD_PARAMS[record_type]['value_param']
- ret_dict[key] = [getattr(elem, record_value_param) for elem in records[key]]
+ params = [RECORD_PARAMS[record_type]['value_param'], 'ttl', 'zone', 'fqdn']
+ ret_dict[key] = []
+ properties = {}
+ for elem in records[key]:
+ for param in params:
+ properties[param] = getattr(elem, param)
+ ret_dict[key].append(properties)
+
return ret_dict
+def compare_record_values(record_type_key, user_record_value, dyn_values):
+ ''' Verify the user record_value exists in dyn'''
+ rtype = get_record_type(record_type_key)
+ for record in dyn_values[record_type_key]:
+ if user_record_value in record[RECORD_PARAMS[rtype]['value_param']]:
+ return True
+
+ return False
+
+def compare_record_ttl(record_type_key, user_record_value, dyn_values, user_param_ttl):
+ ''' Verify the ttls match for the record'''
+ rtype = get_record_type(record_type_key)
+ for record in dyn_values[record_type_key]:
+ # find the right record
+ if user_record_value in record[RECORD_PARAMS[rtype]['value_param']]:
+ # Compare ttls from the records
+ if int(record['ttl']) == user_param_ttl:
+ return True
+
+ return False
+
def main():
'''Ansible module for managing Dyn DNS records.'''
module = AnsibleModule(
@@ -187,16 +237,20 @@ def main():
record_type=dict(required=False, type='str', choices=[
'A', 'AAAA', 'CNAME', 'PTR', 'TXT']),
record_value=dict(required=False, type='str'),
- record_ttl=dict(required=False, default=0, type='int'),
+ record_ttl=dict(required=False, default=None, type='int'),
+ use_zone_ttl=dict(required=False, default=False),
),
required_together=(
['record_fqdn', 'record_value', 'record_ttl', 'record_type']
- )
+ ),
+ mutually_exclusive=[('record_ttl', 'use_zone_ttl')]
)
if IMPORT_ERROR:
- module.fail_json(msg="Unable to import dyn module: https://pypi.python.org/pypi/dyn",
- error=IMPORT_ERROR)
+ module.fail_json(msg="Unable to import dyn module: https://pypi.python.org/pypi/dyn", error=IMPORT_ERROR)
+
+ if module.params['record_ttl'] != None and int(module.params['record_ttl']) <= 0:
+ module.fail_json(msg="Invalid Value for record TTL")
# Start the Dyn session
try:
@@ -204,22 +258,16 @@ def main():
module.params['user_name'],
module.params['user_password'])
except dyn.tm.errors.DynectAuthError as error:
- module.fail_json(msg='Unable to authenticate with Dyn',
- error=str(error))
+ module.fail_json(msg='Unable to authenticate with Dyn', error=str(error))
# Retrieve zone object
try:
dyn_zone = Zone(module.params['zone'])
except dyn.tm.errors.DynectGetError as error:
if 'No such zone' in str(error):
- module.fail_json(
- msg="Not a valid zone for this account",
- zone=module.params['zone']
- )
+ module.fail_json(msg="Not a valid zone for this account", zone=module.params['zone'])
else:
- module.fail_json(msg="Unable to retrieve zone",
- error=str(error))
-
+ module.fail_json(msg="Unable to retrieve zone", error=str(error))
# To retrieve the node object we need to remove the zone name from the FQDN
dyn_node_name = module.params['record_fqdn'].replace('.' + module.params['zone'], '')
@@ -233,27 +281,46 @@ def main():
# All states will need a list of the exiting records for the zone.
dyn_node_records = get_any_records(module, dyn_node)
+ dyn_values = get_record_values(dyn_node_records)
+
if module.params['state'] == 'list':
- module.exit_json(changed=False,
- records=get_record_values(
- dyn_node_records,
- ))
+ module.exit_json(changed=False, dyn_records=dyn_values)
- if module.params['state'] == 'present':
+ elif module.params['state'] == 'absent':
+ # If there are any records present we'll want to delete the node.
+ if dyn_node_records:
+ dyn_node.delete()
+
+ # Publish the zone since we've modified it.
+ dyn_zone.publish()
+
+ module.exit_json(changed=True, msg="Removed node %s from zone %s" % (dyn_node_name, module.params['zone']))
+
+ module.exit_json(changed=False)
+
+ elif module.params['state'] == 'present':
+
+ # configure the TTL variable:
+ # if use_zone_ttl, use the default TTL of the account.
+ # if TTL == None, don't check it, set it as 0 (api default)
+ # if TTL > 0, ensure this TTL is set
+ if module.params['use_zone_ttl']:
+ user_param_ttl = dyn_zone.ttl
+ elif not module.params['record_ttl']:
+ user_param_ttl = 0
+ else:
+ user_param_ttl = module.params['record_ttl']
# First get a list of existing records for the node
- values = get_record_values(dyn_node_records)
- value_key = get_record_key(module.params['record_type'])
- param_value = module.params['record_value']
+ record_type_key = get_record_key(module.params['record_type'])
+ user_record_value = module.params['record_value']
# Check to see if the record is already in place before doing anything.
- if (dyn_node_records and
- dyn_node_records[value_key][0].ttl == module.params['record_ttl'] and
- (param_value in values[value_key] or
- param_value + '.' in values[value_key])):
-
- module.exit_json(changed=False)
+ if dyn_node_records and compare_record_values(record_type_key, user_record_value, dyn_values):
+ if user_param_ttl == 0 or \
+ compare_record_ttl(record_type_key, user_record_value, dyn_values, user_param_ttl):
+ module.exit_json(changed=False, dyn_record=dyn_values)
# Working on the assumption that there is only one record per
# node we will first delete the node if there are any records before
@@ -262,27 +329,20 @@ def main():
dyn_node.delete()
# Now lets create the correct node entry.
- dyn_zone.add_record(dyn_node_name,
- module.params['record_type'],
- module.params['record_value'],
- module.params['record_ttl']
- )
+ record = dyn_zone.add_record(dyn_node_name,
+ module.params['record_type'],
+ module.params['record_value'],
+ user_param_ttl
+ )
# Now publish the zone since we've updated it.
dyn_zone.publish()
- module.exit_json(changed=True,
- msg="Created node %s in zone %s" % (dyn_node_name, module.params['zone']))
- if module.params['state'] == 'absent':
- # If there are any records present we'll want to delete the node.
- if dyn_node_records:
- dyn_node.delete()
- # Publish the zone since we've modified it.
- dyn_zone.publish()
- module.exit_json(changed=True,
- msg="Removed node %s from zone %s" % (dyn_node_name, module.params['zone']))
- else:
- module.exit_json(changed=False)
+ rmsg = "Created node [%s] " % dyn_node_name
+ rmsg += "in zone: [%s]" % module.params['zone']
+ module.exit_json(changed=True, msg=rmsg, dyn_record=get_record_values({record_type_key: [record]}))
+
+ module.fail_json(msg="Unknown state: [%s]" % module.params['state'])
# Ansible tends to need a wild card import so we'll use it here
# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled