pdns.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import requests
  2. import json
  3. from desecapi import settings
  4. headers = {
  5. 'User-Agent': 'desecapi',
  6. 'X-API-Key': settings.POWERDNS_API_TOKEN,
  7. }
  8. def normalize_hostname(name):
  9. if '/' in name or '?' in name:
  10. raise Exception('Invalid hostname ' + name)
  11. return name if name.endswith('.') else name + '.'
  12. def _pdns_post(url, body):
  13. r = requests.post(settings.POWERDNS_API + url, data=json.dumps(body), headers=headers)
  14. if r.status_code < 200 or r.status_code >= 300:
  15. raise Exception(r.text)
  16. return r
  17. def _pdns_patch(url, body):
  18. r = requests.patch(settings.POWERDNS_API + url, data=json.dumps(body), headers=headers)
  19. if r.status_code < 200 or r.status_code >= 300:
  20. raise Exception(r.text)
  21. return r
  22. def _pdns_get(url):
  23. r = requests.get(settings.POWERDNS_API + url, headers=headers)
  24. if (r.status_code < 200 or r.status_code >= 500):
  25. raise Exception(r.text)
  26. return r
  27. def _delete_or_replace_rrset(name, type, value, ttl=60):
  28. """
  29. Return pdns API json to either replace or delete a record set, depending on value is empty or not.
  30. """
  31. if value != "":
  32. return \
  33. {
  34. "records": [
  35. {
  36. "type": type,
  37. "name": name,
  38. "disabled": False,
  39. "content": value,
  40. }
  41. ],
  42. "ttl": ttl,
  43. "changetype": "REPLACE",
  44. "type": type,
  45. "name": name,
  46. }
  47. else:
  48. return \
  49. {
  50. "changetype": "DELETE",
  51. "type": type,
  52. "name": name
  53. }
  54. def create_zone(name, kind='NATIVE'):
  55. """
  56. Commands pdns to create a zone with the given name.
  57. """
  58. payload = {
  59. "name": normalize_hostname(name),
  60. "kind": kind.upper(),
  61. "masters": [],
  62. "nameservers": [
  63. "ns1.desec.io.",
  64. "ns2.desec.io."
  65. ]
  66. }
  67. _pdns_post('/zones', payload)
  68. def zone_exists(name):
  69. """
  70. Returns whether pdns knows a zone with the given name.
  71. """
  72. reply = _pdns_get('/zones/' + normalize_hostname(name))
  73. if reply.status_code == 200:
  74. return True
  75. elif reply.status_code == 422 and 'Could not find domain' in reply.text:
  76. return False
  77. else:
  78. raise Exception(reply.text)
  79. def set_dyn_records(name, a, aaaa):
  80. """
  81. Commands pdns to set the A and AAAA record for the zone with the given name to the given record values.
  82. Only supports one A, one AAAA record.
  83. If a or aaaa is None, pdns will be commanded to delete the record.
  84. """
  85. name = normalize_hostname(name)
  86. _pdns_patch('/zones/' + name, {
  87. "rrsets": [
  88. _delete_or_replace_rrset(name, 'a', a),
  89. _delete_or_replace_rrset(name, 'aaaa', aaaa),
  90. ]
  91. })