nat_limit_watch.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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 __future__ import print_function
  27. import paramiko
  28. import os
  29. from sparker import Sparker, MessageType # type: ignore
  30. import time
  31. import re
  32. import json
  33. import CLEUCreds # type: ignore
  34. from cleu.config import Config as C # type: ignore
  35. SPARK_ROOM = "Core Alarms"
  36. CACHE_FILE = "/home/jclarke/nat_limit.dat"
  37. def send_command(chan, command):
  38. chan.sendall(command + "\n")
  39. time.sleep(0.5)
  40. output = ""
  41. i = 0
  42. while i < 60:
  43. r = chan.recv(65535)
  44. if len(r) == 0:
  45. raise EOFError("Remote host has closed the connection")
  46. r = r.decode("utf-8", "ignore")
  47. output += r
  48. if re.search(r"[#>]$", r.strip()):
  49. break
  50. time.sleep(1)
  51. return output
  52. if __name__ == "__main__":
  53. prev_state = {}
  54. curr_state = {}
  55. spark = Sparker(token=CLEUCreds.SPARK_TOKEN)
  56. if os.path.exists(CACHE_FILE):
  57. with open(CACHE_FILE, "r") as fd:
  58. prev_state = json.load(fd)
  59. ssh_client = paramiko.SSHClient()
  60. ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  61. routers = ["CORE1-EDGE", "CORE2-EDGE"]
  62. for router in routers:
  63. try:
  64. ssh_client.connect(
  65. router,
  66. username=CLEUCreds.NET_USER,
  67. password=CLEUCreds.NET_PASS,
  68. timeout=60,
  69. allow_agent=False,
  70. look_for_keys=False,
  71. )
  72. chan = ssh_client.invoke_shell()
  73. try:
  74. send_command(chan, "term length 0")
  75. send_command(chan, "term width 0")
  76. except:
  77. pass
  78. output = ""
  79. try:
  80. output = send_command(chan, "show ip nat limit all-host | inc [0-9] +[1-9][0-9]+[^0-9]+$")
  81. except Exception as ie:
  82. print(f"Failed to get NAT limit from {router}: {ie}")
  83. continue
  84. for line in output.split("\n"):
  85. m = re.search(r"^(\d+\.\d+\.\d+\.\d+)\s+\d+\s+\d+\s+(\d+)", line)
  86. if m:
  87. host = m.group(1)
  88. misses = m.group(2)
  89. if host not in prev_state:
  90. spark.post_to_spark(
  91. C.WEBEX_TEAM,
  92. SPARK_ROOM,
  93. "Host **{}** has exceeded its NAT connection limit **{}** times".format(host, misses),
  94. MessageType.BAD,
  95. )
  96. curr_state[host] = int(misses)
  97. for host, misses in list(prev_state.items()):
  98. if host not in curr_state:
  99. spark.post_to_spark(
  100. C.WEBEX_TEAM, SPARK_ROOM, "Host **{}** has aged out of the NAT limit exceeded table".format(host), MessageType.GOOD
  101. )
  102. except Exception as e:
  103. ssh_client.close()
  104. print(f"Failed to get NAT limits from {router}: {e}")
  105. continue
  106. ssh_client.close()
  107. with open(CACHE_FILE, "w") as fd:
  108. json.dump(curr_state, fd, indent=4)