yp-to-pro.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env python
  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. from flask import Flask
  27. from flask import Response
  28. from ncclient import manager
  29. from ncclient.operations import RPCError
  30. import xmltodict
  31. import sys
  32. import lxml.etree as ET
  33. from argparse import ArgumentParser
  34. import json
  35. app = Flask(__name__)
  36. CONFIG_VERBOSE = False
  37. CONFIG_OPER_ID = 0
  38. curr_rtt = -1
  39. @app.route('/')
  40. def get_stats():
  41. global curr_rtt
  42. return Response('rtt {}\n'.format(curr_rtt), mimetype='text/plain')
  43. def callback(notif):
  44. global curr_rtt, CONFIG_OPER_ID, CONFIG_VERBOSE
  45. d = xmltodict.parse(notif.xml)
  46. if CONFIG_VERBOSE:
  47. print(json.dumps(d, indent=4))
  48. opers = []
  49. if isinstance(d['notification']['push-update']['datastore-contents-xml']['ip-sla-stats']['sla-oper-entry'], list):
  50. opers = d['notification'][
  51. 'push-update']['datastore-contents-xml']['ip-sla-stats']['sla-oper-entry']
  52. else:
  53. opers.append(d['notification']['push-update']
  54. ['datastore-contents-xml']['ip-sla-stats']['sla-oper-entry'])
  55. found_rtt = False
  56. for oper in opers:
  57. if CONFIG_VERBOSE:
  58. print('Notif oper-id: {}'.format(oper['oper-id']))
  59. if int(oper['oper-id']) == CONFIG_OPER_ID:
  60. if CONFIG_VERBOSE:
  61. print('Setting curr_rtt to {}'.format(
  62. oper['rtt-info']['latest-rtt']['rtt']))
  63. curr_rtt = int(oper['rtt-info']['latest-rtt']['rtt'])
  64. found_rtt = True
  65. break
  66. if not found_rtt:
  67. curr_rtt = -1
  68. def errback(e):
  69. print('Error : {}'.format(e))
  70. def subscribe(dev, user, password, port=830, timeout=90, period=1000):
  71. global app, CONFIG_VERBOSE
  72. with manager.connect(host=dev, port=port, username=user, password=password, timeout=timeout, hostkey_verify=False) as m:
  73. try:
  74. response = m.establish_subscription(
  75. callback, errback, '/ip-sla-ios-xe-oper:ip-sla-stats/sla-oper-entry', period).xml
  76. data = ET.fromstring(response.encode('utf-8'))
  77. if CONFIG_VERBOSE:
  78. print(ET.tostring(data, pretty_print=True))
  79. app.run()
  80. except RPCError as e:
  81. print('RPC Error subscribing to stream: {}'.format(e._raw))
  82. sys.exit(1)
  83. if __name__ == '__main__':
  84. parser = ArgumentParser(description='usage:')
  85. parser.add_argument('-a', '--host', type=str, required=True,
  86. help='Device IP address or hostname')
  87. parser.add_argument('-u', '--username', type=str, required=True,
  88. help='Device username (NETCONF server username)')
  89. parser.add_argument('-p', '--password', type=str, required=True,
  90. help='Device password (NETCONF server password)')
  91. parser.add_argument('-t', '--timeout', type=int, default=90,
  92. help='NETCONF server connection timeout')
  93. parser.add_argument('-o', '--oper-id', type=int,
  94. required=True, help='IP SLA operation ID to monitor')
  95. parser.add_argument('--period', type=int, default=1000,
  96. help='Period to wait for notification pushes')
  97. parser.add_argument('-v', '--verbose', action='store_true',
  98. help='Enable verbose output')
  99. parser.add_argument('--port', type=int, default=830,
  100. help='NETCONF server port')
  101. args = parser.parse_args()
  102. CONFIG_VERBOSE = args.verbose
  103. CONFIG_OPER_ID = args.oper_id
  104. if CONFIG_VERBOSE:
  105. print('Oper ID: {}'.format(CONFIG_OPER_ID))
  106. subscribe(args.host, args.username, args.password,
  107. args.port, args.timeout, args.period)