pdns.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import requests
  2. import json
  3. from desecapi import settings
  4. from desecapi.exceptions import PdnsException
  5. headers_nslord = {
  6. 'Accept': 'application/json',
  7. 'User-Agent': 'desecapi',
  8. 'X-API-Key': settings.NSLORD_PDNS_API_TOKEN,
  9. }
  10. headers_nsmaster = {
  11. 'User-Agent': 'desecapi',
  12. 'X-API-Key': settings.NSMASTER_PDNS_API_TOKEN,
  13. }
  14. def _pdns_delete(url):
  15. # We first delete the zone from nslord, the main authoritative source of our DNS data.
  16. # However, we do not want to wait for the zone to expire on the slave ("nsmaster").
  17. # We thus issue a second delete request on nsmaster to delete the zone there immediately.
  18. r1 = requests.delete(settings.NSLORD_PDNS_API + url, headers=headers_nslord)
  19. if r1.status_code < 200 or r1.status_code >= 300:
  20. # Deletion technically does not fail if the zone didn't exist in the first place
  21. if r1.status_code == 422 and 'Could not find domain' in r1.text:
  22. pass
  23. else:
  24. raise PdnsException(r1)
  25. # Delete from nsmaster as well
  26. r2 = requests.delete(settings.NSMASTER_PDNS_API + url, headers=headers_nsmaster)
  27. if r2.status_code < 200 or r2.status_code >= 300:
  28. # Deletion technically does not fail if the zone didn't exist in the first place
  29. if r2.status_code == 422 and 'Could not find domain' in r2.text:
  30. pass
  31. else:
  32. raise PdnsException(r2)
  33. return (r1, r2)
  34. def _pdns_post(url, body):
  35. r = requests.post(settings.NSLORD_PDNS_API + url, data=json.dumps(body), headers=headers_nslord)
  36. if r.status_code < 200 or r.status_code >= 300:
  37. raise PdnsException(r)
  38. return r
  39. def _pdns_patch(url, body):
  40. r = requests.patch(settings.NSLORD_PDNS_API + url, data=json.dumps(body), headers=headers_nslord)
  41. if r.status_code < 200 or r.status_code >= 300:
  42. raise PdnsException(r)
  43. return r
  44. def _pdns_get(url):
  45. r = requests.get(settings.NSLORD_PDNS_API + url, headers=headers_nslord)
  46. if r.status_code < 200 or r.status_code >= 400:
  47. raise PdnsException(r)
  48. return r
  49. def _pdns_put(url):
  50. r = requests.put(settings.NSLORD_PDNS_API + url, headers=headers_nslord)
  51. if r.status_code < 200 or r.status_code >= 500:
  52. raise PdnsException(r)
  53. return r
  54. def create_zone(domain, nameservers, kind='NATIVE'):
  55. """
  56. Commands pdns to create a zone with the given name and nameservers.
  57. """
  58. name = domain.name
  59. if not name.endswith('.'):
  60. name += '.'
  61. payload = {'name': name, 'kind': kind.upper(), 'masters': [],
  62. 'nameservers': nameservers}
  63. _pdns_post('/zones', payload)
  64. def delete_zone(domain):
  65. """
  66. Commands pdns to delete a zone with the given name.
  67. """
  68. _pdns_delete('/zones/' + domain.pdns_id)
  69. def get_keys(domain):
  70. """
  71. Retrieves a dict representation of the DNSSEC key information
  72. """
  73. try:
  74. r = _pdns_get('/zones/%s/cryptokeys' % domain.pdns_id)
  75. keys = [{k: key[k] for k in ('dnskey', 'ds', 'flags', 'keytype')}
  76. for key in r.json()
  77. if key['active'] and key['keytype'] in ['csk', 'ksk']]
  78. except:
  79. keys = []
  80. return keys
  81. def get_zone(domain):
  82. """
  83. Retrieves a dict representation of the zone from pdns
  84. """
  85. r = _pdns_get('/zones/' + domain.pdns_id)
  86. return r.json()
  87. def get_rrset_datas(domain):
  88. """
  89. Retrieves a dict representation of the RRsets in a given zone
  90. """
  91. return [{'domain': domain,
  92. 'subname': rrset['name'][:-(len(domain.name) + 2)],
  93. 'type': rrset['type'],
  94. 'records_data': [{'content': record['content']}
  95. for record in rrset['records']],
  96. 'ttl': rrset['ttl']}
  97. for rrset in get_zone(domain)['rrsets']]
  98. def set_rrset(rrset, notify=True):
  99. return set_rrsets(rrset.domain, [rrset], notify=notify)
  100. def set_rrsets(domain, rrsets, notify=True):
  101. data = {'rrsets':
  102. [{'name': rrset.name, 'type': rrset.type, 'ttl': rrset.ttl,
  103. 'changetype': 'REPLACE',
  104. 'records': [{'content': record.content, 'disabled': False}
  105. for record in rrset.records.all()]
  106. }
  107. for rrset in rrsets]
  108. }
  109. _pdns_patch('/zones/' + domain.pdns_id, data)
  110. if notify:
  111. notify_zone(domain)
  112. def notify_zone(domain):
  113. """
  114. Commands pdns to notify the zone to the pdns slaves.
  115. """
  116. _pdns_put('/zones/%s/notify' % domain.pdns_id)