check_arista.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/env python
  2. # Author: Jon Schipp <jonschipp@gmail.com, jschipp@illinois.edu>
  3. import sys
  4. import os
  5. import argparse
  6. import json
  7. import filecmp
  8. import time
  9. from jsonrpclib import Server
  10. # Nagios exit codes
  11. NAGIOS_OK = 0
  12. NAGIOS_WARNING = 1
  13. NAGIOS_CRITICAL = 2
  14. NAGIOS_UNKNOWN = 3
  15. # Build accepted check type options for argparse
  16. status_checks=['protocol_status', 'interface_status', 'duplex_status', 'bandwidth_status']
  17. rate_checks=['input_rate', 'output_rate']
  18. other_checks=['dumbno', 'link_status', 'traffic_status']
  19. all_checks= status_checks + rate_checks + other_checks
  20. result = NAGIOS_OK
  21. # Create dictionaries to minimize code
  22. DIRECTION_MAP = {
  23. 'input_rate': 'inBitsRate',
  24. 'output_rate': 'outBitsRate',
  25. }
  26. INTERFACE_MAP = {
  27. 'input_rate': '9/1-24',
  28. 'output_rate': '3/1-16',
  29. }
  30. STATUS_MAP = {
  31. 'interface_status': 'interfaceStatus',
  32. 'protocol_status': 'lineProtocolStatus',
  33. 'duplex_status': 'duplex',
  34. 'bandwidth_status': 'bandwidth',
  35. }
  36. STATUS_MSG = {
  37. 0: 'OK',
  38. 1: 'WARNING',
  39. 2: 'CRITICAL',
  40. 3: 'UNKNOWN',
  41. }
  42. def cred_usage():
  43. doc = '''
  44. Could not open file! Does it exist? Is it valid JSON?
  45. A file containing the API credentials in JSON should be read in using ``-f <file>''
  46. Its contents should be formatted like this:
  47. {
  48. "user":"aristauser",
  49. "password":"asdfasdfasdfasdf"
  50. }
  51. '''[1:]
  52. return doc
  53. def check_rate(switch, direction, interfaces, skip):
  54. response = switch.runCmds( 1, ["show interfaces Ethernet" + interfaces] )
  55. ifs = response[0]["interfaces"]
  56. d={}
  57. rc=[]
  58. for p,info in ifs.items():
  59. if p in skip:
  60. continue
  61. if info["description"] is None:
  62. continue
  63. rate = info["interfaceStatistics"].get(direction, 0) / (1000**2)
  64. d[p] = [rate, threshold(rate)]
  65. for nic in d:
  66. status=d[nic][1]
  67. rc.append(status)
  68. print "%s: %s Mbps: %.2f" % (STATUS_MSG[status], nic, d[nic][0])
  69. return rc
  70. def check_status(switch, option, devices, skip):
  71. status_type=option
  72. crit_items=["connected", "up", "duplexFull", 10000000000]
  73. response = switch.runCmds( 1, ["show interfaces"])
  74. ifs = response[0]["interfaces"]
  75. result = NAGIOS_OK
  76. for p,info in ifs.items():
  77. if p in skip:
  78. continue
  79. if status_type == "bandwidth":
  80. bw=crit_items[-1]
  81. if int(info[status_type]) < bw:
  82. print "CRITICAL: %s Bandwidth: %dGbps" % (p, info[status_type] / (1000**3))
  83. result = NAGIOS_CRITICAL
  84. continue
  85. if devices != "None":
  86. if p in devices:
  87. if info[status_type] not in crit_items:
  88. result = NAGIOS_CRITICAL
  89. print "CRITICAL: %s %s" % (p, info[status_type])
  90. continue
  91. else: print "SUCCESS: %s %s" % (p, info[status_type])
  92. if devices == "None":
  93. if info["description"] is None:
  94. continue
  95. if status_type not in info:
  96. continue
  97. if info[status_type] not in crit_items:
  98. result = NAGIOS_CRITICAL
  99. print "CRITICAL: %s %s" % (p, info[status_type])
  100. if result == 0: print "SUCCESS: %s check successful" % status_type
  101. return result
  102. def check_traffic_status(switch, skip):
  103. response = switch.runCmds( 1, ["show interfaces"])
  104. ifs = response[0]["interfaces"]
  105. result = 0
  106. for p,info in ifs.items():
  107. if p in skip:
  108. continue
  109. if info["description"] is None:
  110. continue
  111. if info["lineProtocolStatus"] == "notPresent":
  112. continue
  113. if info["interfaceStatus"] == "notconnect":
  114. continue
  115. in_traffic = info["interfaceStatistics"]["inPktsRate"]
  116. out_traffic = info["interfaceStatistics"]["outPktsRate"]
  117. if in_traffic == 0 and out_traffic == 0:
  118. print "CRITICAL: %s In: %s Out: %s" % (p, in_traffic, out_traffic)
  119. result=1
  120. if result == 1:
  121. return NAGIOS_CRITICAL
  122. else:
  123. print "SUCCESS: Traffic is being processed by all connected interfaces"
  124. return NAGIOS_OK
  125. def check_dumbno(switch, skip):
  126. path = '/root/%s' % os.path.basename(sys.argv[0]) + '-dumbno.state'
  127. current = path + '.current'
  128. old = path + '.old'
  129. response = switch.runCmds( 1, ["enable", "show ip access-lists"] )
  130. acl_lists = response[1]["aclList"]
  131. rules=[]
  132. for list in acl_lists:
  133. name = list["name"]
  134. if name in skip:
  135. continue
  136. for rule in list["sequence"]:
  137. if "permit" in rule["text"]:
  138. continue
  139. line = name + " - " + rule["text"]
  140. rules.append(line)
  141. if os.path.isfile(current):
  142. os.rename(current, old)
  143. save_file(rules, current)
  144. return compare_file(current, old)
  145. def check_link_status(switch, skip):
  146. path = '/root/%s' % os.path.basename(sys.argv[0]) + '-flap.state'
  147. current = path + '.current'
  148. old = path + '.old'
  149. key = "lastStatusChangeTimestamp"
  150. response = switch.runCmds( 1, ["show interfaces"])
  151. ifs = response[0]["interfaces"]
  152. data=[]
  153. for p,info in ifs.items():
  154. if p in skip:
  155. continue
  156. if key not in info:
  157. continue
  158. print p, info[key]
  159. if os.path.isfile(current):
  160. os.rename(current, old)
  161. save_file(data, current)
  162. return compare_file(current, old)
  163. def compare_file(current, old):
  164. if not os.path.isfile(current):
  165. print "First run, waiting to create history"
  166. return NAGIOS_UNKNOWN
  167. if not os.path.isfile(old):
  168. return NAGIOS_UNKNOWN
  169. if filecmp.cmp(current, old):
  170. print "CRITICAL: Entries haven't changed"
  171. return NAGIOS_CRITICAL
  172. print "SUCCESS: Entries rules have changed"
  173. return NAGIOS_OK
  174. def save_file(data, path):
  175. file = "\n".join(data)
  176. try:
  177. with open(path, 'w') as f:
  178. f.write(file)
  179. except IOError:
  180. print "Unable to open file for writing"
  181. exit(NAGIOS_UNKNOWN)
  182. def threshold(value):
  183. if value >= crit:
  184. return NAGIOS_CRITICAL
  185. if value >= warn:
  186. return NAGIOS_WARNING
  187. return NAGIOS_OK
  188. def arguments():
  189. global crit
  190. global warn
  191. parser = argparse.ArgumentParser(description='Check Arista stats')
  192. parser.add_argument("-s", "--skip", type=str, help="Items to skip from check")
  193. parser.add_argument("-d", "--device", type=str, help="Devices to check (def: all) (sep: ,) e.g. Ethernet1/1/3")
  194. parser.add_argument("-T", "--type", type=str, help="Type of check", choices=all_checks)
  195. parser.add_argument("-H", "--host", type=str, help="<host:port> e.g. arista.company.org:443", required=True)
  196. parser.add_argument("-f", "--filename", type=str, help="Filename that contains API credentials", required=True)
  197. parser.add_argument("-c", "--critical", type=int, help="Critical value in Mbps")
  198. parser.add_argument("-w", "--warning", type=int, help="Warning value in Mbps")
  199. args = parser.parse_args()
  200. option = args.type
  201. host = args.host
  202. filename = args.filename
  203. crit = args.critical
  204. warn = args.warning
  205. if args.skip:
  206. skip = args.skip.split(",")
  207. else:
  208. skip = "None"
  209. if args.device:
  210. devices = args.device.split(",")
  211. else:
  212. devices = "None"
  213. return(option, host, filename, devices, skip)
  214. def get_creds(filename):
  215. try:
  216. with open(filename, 'r') as f:
  217. creds = json.load(f)
  218. return creds
  219. except IOError:
  220. print cred_usage()
  221. exit(NAGIOS_UNKNOWN)
  222. def main():
  223. option, host, filename, devices, skip = arguments()
  224. creds = get_creds(filename)
  225. user = creds["user"]
  226. password = creds["password"]
  227. url = 'https://' + user + ':' + password + host + '/command-api'
  228. switch = Server(url)
  229. if option == "dumbno":
  230. exit(check_dumbno(switch, skip))
  231. elif option == "traffic_status":
  232. exit(check_traffic_status(switch, skip))
  233. elif option == "link_status":
  234. exit(check_link_status(switch, skip))
  235. elif option in status_checks:
  236. option = STATUS_MAP[option]
  237. exit(check_status(switch, option, devices, skip))
  238. elif option in rate_checks:
  239. direction = DIRECTION_MAP[option]
  240. interfaces = INTERFACE_MAP[option]
  241. exit(max(check_rate(switch, direction, interfaces, skip)))
  242. else:
  243. print "Invalid option"
  244. exit(NAGIOS_UNKNOWN)
  245. if __name__ == "__main__":
  246. main()