dns-tester.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #!/usr/bin/env python2
  2. #
  3. # Copyright (c) 2017-2018 Joe Clarke <jclarke@cisco.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. # 1. Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. # SUCH DAMAGE.
  26. import dns
  27. import dns.resolver
  28. from sparker import Sparker
  29. import os
  30. from subprocess import Popen, PIPE
  31. import shlex
  32. import json
  33. SPARK_ROOM = 'Core Alarms'
  34. SPARK_TEAM = 'CL17-Infra_team'
  35. CACHE_FILE = '/home/jclarke/dns_cache.dat'
  36. def report_error(server, obj):
  37. global SPARK_ROOM, SPARK_TEAM
  38. spark = Sparker()
  39. msg = 'DNS failure to {}\n\n'
  40. msg += '```\n'
  41. msg += '{}\n'
  42. msg += '```'
  43. res = spark.post_to_spark(SPARK_TEAM, SPARK_ROOM, msg.format(server, obj))
  44. if not res:
  45. print('Error posting to Spark!')
  46. def report_good(msg):
  47. global SPARK_ROOM, SPARK_TEAM
  48. spark = Sparker()
  49. res = spark.post_to_spark(SPARK_TEAM, SPARK_ROOM, msg)
  50. if not res:
  51. print('Error posting to Spark!')
  52. dns_servers = ['10.100.253.6', '10.100.253.106']
  53. rdns = '2a01:4f8:120:7261:216:3eff:fe44:2015'
  54. curr_state = {}
  55. prev_state = {}
  56. if os.path.exists(CACHE_FILE):
  57. fd = open(CACHE_FILE, 'r')
  58. prev_state = json.load(fd)
  59. fd.close()
  60. res = os.system('/usr/local/sbin/fping6 -q -r0 {}'.format(rdns))
  61. if res != 0:
  62. if rdns in prev_state and prev_state[rdns]:
  63. proc = Popen(shlex.split(
  64. '/usr/sbin/traceroute6 -q 1 -n -m 30 {}'.format(rdns)), stdout=PIPE, stderr=PIPE)
  65. out, err = proc.communicate()
  66. report_error(
  67. rdns, 'Remote DNS server is not pingable; current traceroute:\n{}'.format(out))
  68. curr_state[rdns] = False
  69. else:
  70. curr_state[rdns] = True
  71. if rdns in prev_state and not prev_state[rdns]:
  72. proc = Popen(shlex.split(
  73. '/usr/sbin/traceroute6 -q 1 -n -m 30 {}'.format(rdns)), stdout=PIPE, stderr=PIPE)
  74. out, err = proc.communicate()
  75. report_good('{} is pingable again; current traceroute:\n```\n{}\n```'.format(rdns, out))
  76. for ds in dns_servers:
  77. resolv = dns.resolver.Resolver()
  78. resolv.timeout = 2
  79. resolv.lifetime = 2
  80. resolv.nameservers = [ds]
  81. try:
  82. ans = resolv.query('ciscolive-test.local', 'AAAA')
  83. if ans.response.rcode() != dns.rcode.NOERROR:
  84. curr_state[ds] = False
  85. if ds in prev_state and prev_state[ds]:
  86. report_error(ds, ans.response)
  87. else:
  88. curr_state[ds] = True
  89. if ds in prev_state and not prev_state[ds]:
  90. report_good('{} is now resolving fine'.format(ds, ds))
  91. except Exception as e:
  92. curr_state[ds] = False
  93. if ds in prev_state and prev_state[ds]:
  94. report_error(ds, e)
  95. fd = open(CACHE_FILE, 'w')
  96. json.dump(curr_state, fd, indent=4)
  97. fd.close()