Browse Source

safety check for azure null values

pull/84/head
Heesu Hwang 9 years ago
parent
commit
08d3fda99e
1 changed files with 61 additions and 43 deletions
  1. +61
    -43
      octodns/provider/azuredns.py

+ 61
- 43
octodns/provider/azuredns.py View File

@ -11,7 +11,7 @@ from azure.mgmt.dns.models import ARecord, AaaaRecord, CnameRecord, MxRecord, \
SrvRecord, NsRecord, PtrRecord, TxtRecord, Zone SrvRecord, NsRecord, PtrRecord, TxtRecord, Zone
from functools import reduce from functools import reduce
import sys
import logging import logging
from ..record import Record from ..record import Record
from .base import BaseProvider from .base import BaseProvider
@ -26,7 +26,7 @@ class _AzureRecord(object):
Azure. Azure.
''' '''
def __init__(self, resource_group, record, values=None):
def __init__(self, resource_group, record, delete=False):
''' '''
:param resource_group: The name of resource group in Azure :param resource_group: The name of resource group in Azure
:type resource_group: str :type resource_group: str
@ -43,14 +43,16 @@ class _AzureRecord(object):
self.relative_record_set_name = record.name or '@' self.relative_record_set_name = record.name or '@'
self.record_type = record._type self.record_type = record._type
data = values or record.data
if delete:
return
format_u_s = '' if record._type == 'A' else '_' format_u_s = '' if record._type == 'A' else '_'
key_name = '{}{}records'.format(self.record_type, format_u_s).lower() key_name = '{}{}records'.format(self.record_type, format_u_s).lower()
class_name = '{}'.format(self.record_type).capitalize() + \ class_name = '{}'.format(self.record_type).capitalize() + \
'Record'.format(self.record_type) 'Record'.format(self.record_type)
self.params = getattr(self, '_params_for_{}'.format(record._type)) self.params = getattr(self, '_params_for_{}'.format(record._type))
self.params = self.params(data, key_name, eval(class_name))
self.params = self.params(record.data, key_name, eval(class_name))
self.params['ttl'] = record.ttl self.params['ttl'] = record.ttl
def _params(self, data, key_name, azure_class): def _params(self, data, key_name, azure_class):
@ -71,7 +73,7 @@ class _AzureRecord(object):
vals['weight'], vals['weight'],
vals['port'], vals['port'],
vals['target'])) vals['target']))
else:
else: # single value at key 'value'
params.append(azure_class(data['value']['priority'], params.append(azure_class(data['value']['priority'],
data['value']['weight'], data['value']['weight'],
data['value']['port'], data['value']['port'],
@ -82,17 +84,23 @@ class _AzureRecord(object):
params = [] params = []
if 'values' in data: if 'values' in data:
for vals in data['values']: for vals in data['values']:
params.append(azure_class(vals['priority'],
vals['value']))
else:
params.append(azure_class(data['value']['priority'],
data['value']['value']))
params.append(azure_class(vals['preference'],
vals['exchange']))
else: # single value at key 'value'
params.append(azure_class(data['value']['preference'],
data['value']['exchange']))
return {key_name: params} return {key_name: params}
def _params_for_CNAME(self, data, key_name, azure_class): def _params_for_CNAME(self, data, key_name, azure_class):
return {'cname_record': CnameRecord(data['value'])} return {'cname_record': CnameRecord(data['value'])}
def _equals(self, b): def _equals(self, b):
'''Checks whether two records are equal by comparing all fields.
:param b: Another _AzureRecord object
:type b: _AzureRecord
:type return: bool
'''
def parse_dict(params): def parse_dict(params):
vals = [] vals = []
for char in params: for char in params:
@ -114,6 +122,9 @@ class _AzureRecord(object):
(self.relative_record_set_name == b.relative_record_set_name) (self.relative_record_set_name == b.relative_record_set_name)
def __str__(self): def __str__(self):
'''String representation of an _AzureRecord.
:type return: str
'''
string = 'Zone: {}; '.format(self.zone_name) string = 'Zone: {}; '.format(self.zone_name)
string += 'Name: {}; '.format(self.relative_record_set_name) string += 'Name: {}; '.format(self.relative_record_set_name)
string += 'Type: {}; '.format(self.record_type) string += 'Type: {}; '.format(self.record_type)
@ -128,6 +139,10 @@ class _AzureRecord(object):
return string return string
def period_validate(string):
return string if string.endswith('.') else string + '.'
class AzureProvider(BaseProvider): class AzureProvider(BaseProvider):
''' '''
Azure DNS Provider Azure DNS Provider
@ -213,22 +228,24 @@ class AzureProvider(BaseProvider):
''' '''
Required function of manager.py. Required function of manager.py.
Special notes for Azure. Azure zone names omit final '.'
Azure record names for '' are represented by '@'
Special notes for Azure.
Azure zone names omit final '.'
Azure root records names are represented by '@'. OctoDNS uses ''
Azure records created through online interface may have null values Azure records created through online interface may have null values
(eg, no IP address for A record). Specific quirks such as these are
responsible for any strange parsing.
(eg, no IP address for A record).
Azure online interface allows constructing records with null values
which are destroyed by _apply.
Specific quirks such as these are responsible for any non-obvious
parsing in this function and the functions '_params_for_*'.
:param zone: A dns zone :param zone: A dns zone
:type zone: octodns.zone.Zone :type zone: octodns.zone.Zone
:param target: Checks if Azure is source or target of config. :param target: Checks if Azure is source or target of config.
Currently only supports as a target. Does not use. Currently only supports as a target. Does not use.
:type target: bool :type target: bool
TODO: azure interface allows null values. If this attempts to
populate with them, will fail. add safety check (simply delete
records with null values?)
:param lenient: Unused. Check octodns.manager for usage.
:type lenient: bool
:type return: void :type return: void
''' '''
@ -261,40 +278,38 @@ class AzureProvider(BaseProvider):
return {'values': [reduce((lambda a, b: a + b), ar.value) return {'values': [reduce((lambda a, b: a + b), ar.value)
for ar in azrecord.txt_records]} for ar in azrecord.txt_records]}
def _data_for_CNAME(self, azrecord): # TODO: see TODO in pop comment.
def _data_for_CNAME(self, azrecord):
'''Parsing data from Azure DNS Client record call
:param azrecord: a return of a call to list azure records
:type azrecord: azure.mgmt.dns.models.RecordSet
:type return: dict
CNAME and PTR both use the catch block to catch possible empty
records. Refer to population comment.
'''
try: try:
val = azrecord.cname_record.cname
if not val.endswith('.'):
val += '.'
return {'value': val}
return {'value': period_validate(azrecord.cname_record.cname)}
except: except:
return {'value': '.'} # TODO: this is a bad fix. but octo checks
# that cnames have trailing '.' while azure allows creating cnames
# on the online interface with no value.
return {'value': '.'}
def _data_for_PTR(self, azrecord): # TODO: see TODO in population comment.
def _data_for_PTR(self, azrecord):
try: try:
val = azrecord.ptr_records[0].ptdrname
if not val.endswith('.'):
val += '.'
return {'value': val}
return {'value': period_validate(azrecord.ptr_records[0].ptdrname)}
except: except:
return {'value': '.'} return {'value': '.'}
def _data_for_MX(self, azrecord): def _data_for_MX(self, azrecord):
return {'values': [{'priority': ar.preference, 'value': ar.exchange}
for ar in azrecord.mx_records]
}
return {'values': [{'preference': ar.preference,
'exchange': ar.exchange}
for ar in azrecord.mx_records]}
def _data_for_SRV(self, azrecord): def _data_for_SRV(self, azrecord):
return {'values': [{'priority': ar.priority, 'weight': ar.weight, return {'values': [{'priority': ar.priority, 'weight': ar.weight,
'port': ar.port, 'target': ar.target} 'port': ar.port, 'target': ar.target}
for ar in azrecord.srv_records]
}
for ar in azrecord.srv_records]}
def _data_for_NS(self, azrecord): # TODO: see TODO in population comment.
def period_validate(string):
return string if string.endswith('.') else string + '.'
def _data_for_NS(self, azrecord):
vals = [ar.nsdname for ar in azrecord.ns_records] vals = [ar.nsdname for ar in azrecord.ns_records]
return {'values': [period_validate(val) for val in vals]} return {'values': [period_validate(val) for val in vals]}
@ -315,15 +330,18 @@ class AzureProvider(BaseProvider):
record_type=ar.record_type, record_type=ar.record_type,
parameters=ar.params) parameters=ar.params)
print('* Success Create/Update: {}'.format(ar), file=sys.stderr)
_apply_Update = _apply_Create
def _apply_Delete(self, change): def _apply_Delete(self, change):
ar = _AzureRecord(self._resource_group, change.existing)
ar = _AzureRecord(self._resource_group, change.existing, delete=True)
delete = self._dns_client.record_sets.delete delete = self._dns_client.record_sets.delete
delete(self._resource_group, ar.zone_name, ar.relative_record_set_name, delete(self._resource_group, ar.zone_name, ar.relative_record_set_name,
ar.record_type) ar.record_type)
def _apply_Update(self, change):
self._apply_Create(change)
print('* Success Delete: {}'.format(ar), file=sys.stderr)
def _apply(self, plan): def _apply(self, plan):
''' '''


Loading…
Cancel
Save