get-devs.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2017-2020 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. from builtins import range
  27. import requests
  28. from requests.packages.urllib3.exceptions import InsecureRequestWarning # type: ignore
  29. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  30. import json
  31. import time
  32. import os
  33. from subprocess import call
  34. from sparker import Sparker, MessageType # type: ignore
  35. import re
  36. from multiprocessing import Pool
  37. import socket
  38. import CLEUCreds # type: ignore
  39. from cleu.config import Config as C # type: ignore
  40. CACHE_FILE = "/home/jclarke/cached_devs.dat"
  41. PING_DEVS_FILE = "/home/jclarke/ping-devs.json"
  42. MESSAGES = {
  43. "BAD": {"msg": "Pinger detected that device %s (IP: %s)%s is no longer reachable", "type": MessageType.BAD},
  44. "GOOD": {"msg": "Pinger has detected that device %s (IP: %s)%s is now reachable again", "type": MessageType.GOOD},
  45. }
  46. ROOM_NAME = "Device Alarms"
  47. excluded_devices = [r"^VHS-"]
  48. additional_devices = []
  49. def check_prev(dev_dic, prev_devs, pstate="REACHABLE"):
  50. send_msg = False
  51. for pd in prev_devs:
  52. if pd["name"] == dev_dic["name"]:
  53. if pd["reachability"] != pstate:
  54. send_msg = True
  55. break
  56. return send_msg
  57. def know_device(dev_dic, prev_devs):
  58. for pd in prev_devs:
  59. if pd["name"] == dev_dic["name"]:
  60. return True
  61. return False
  62. def ping_device(dev):
  63. global ROOM_NAME, MESSAGES, prev_devs, spark, excluded_devices
  64. dev_dic = {}
  65. dev_dic["name"] = dev["Hostname"]
  66. dev_dic["ip"] = dev["IPAddress"]
  67. if dev_dic["ip"] == "0.0.0.0":
  68. return None
  69. for exc in excluded_devices:
  70. if re.search(exc, dev_dic["name"]) or re.search(exc, dev_dic["ip"]):
  71. return None
  72. # print('Pinging {}'.format(dev_dic['name']))
  73. msg_tag = "BAD"
  74. send_msg = True
  75. if not dev["Reachable"]:
  76. send_msg = know_device(dev_dic, prev_devs)
  77. for _ in range(2):
  78. res = call(["/usr/local/sbin/fping", "-q", "-r0", dev_dic["ip"]])
  79. if res == 0:
  80. break
  81. time.sleep(0.5)
  82. if res != 0:
  83. dev_dic["reachability"] = "UNREACHABLE"
  84. send_msg = check_prev(dev_dic, prev_devs, "UNREACHABLE")
  85. else:
  86. dev_dic["reachability"] = "REACHABLE"
  87. msg_tag = "GOOD"
  88. send_msg = check_prev(dev_dic, prev_devs)
  89. if send_msg:
  90. loc = ""
  91. if "LocationDetail" in dev:
  92. loc = " (Location: {})".format(dev["LocationDetail"])
  93. message = MESSAGES[msg_tag]["msg"] % (dev_dic["name"], dev_dic["ip"], loc)
  94. spark.post_to_spark(C.WEBEX_TEAM, ROOM_NAME, message, MESSAGES[msg_tag]["type"])
  95. return dev_dic
  96. def get_devs(p):
  97. global additional_devices
  98. url = "http://{}/get/switches/json".format(C.TOOL)
  99. devices = []
  100. # response = requests.request('GET', url)
  101. code = 200
  102. # code = response.status_code
  103. if code == 200:
  104. # j = json.loads(response.text)
  105. j = []
  106. for dev in additional_devices:
  107. ip = dev
  108. try:
  109. ip = socket.gethostbyname(dev)
  110. except Exception as e:
  111. spark.post_to_spark(C.WEBEX_TEAM, ROOM_NAME, "Failed to resolve {}: {}".format(dev, e), MessageType.WARNING)
  112. continue
  113. j.append({"Hostname": dev, "IPAddress": ip, "Reachable": True})
  114. results = [p.apply_async(ping_device, [d]) for d in j]
  115. for res in results:
  116. retval = res.get()
  117. if retval is not None:
  118. devices.append(retval)
  119. return devices
  120. if __name__ == "__main__":
  121. prev_devs = []
  122. if os.path.exists(CACHE_FILE):
  123. with open(CACHE_FILE, "r") as fd:
  124. prev_devs = json.load(fd)
  125. spark = Sparker(token=CLEUCreds.SPARK_TOKEN)
  126. try:
  127. with open(PING_DEVS_FILE, "r") as fd:
  128. additional_devices = json.load(fd)
  129. except:
  130. pass
  131. pool = Pool(20)
  132. devs = get_devs(pool)
  133. with open(CACHE_FILE, "w") as fd:
  134. json.dump(devs, fd, ensure_ascii=False, indent=4)