pdns.py 4.3 KB

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