#!/usr/bin/env python3 __author__ = "Mislav Novakovic " __copyright__ = "Copyright 2018, Deutsche Telekom AG" __license__ = "Apache 2.0" # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This sample application demonstrates use of Python programming language bindings for sysrepo library. # Original c application was rewritten in Python to show similarities and differences # between the two. # # Most notable difference is in the very different nature of languages, c is weakly statically typed language # while Python is strongly dynamically typed. Python code is much easier to read and logic easier to comprehend # for smaller scripts. Memory safety is not an issue but lower performance can be expected. # # The original c implementation is also available in the source, so one can refer to it to evaluate trade-offs. import sysrepo as sr import sys # Helper function for printing changes given operation, old and new value. def print_change(op, old_val, new_val): if op == sr.SR_OP_CREATED: print(f"CREATED: {new_val.to_string()}") elif op == sr.SR_OP_DELETED: print(f"DELETED: {old_val.to_string()}") elif op == sr.SR_OP_MODIFIED: print(f"MODIFIED: {old_val.to_string()} to {new_val.to_string()}") elif op == sr.SR_OP_MOVED: print(f"MOVED: {new_val.xpath()} after {old_val.xpath()}") # Helper function for printing events. def ev_to_str(ev): if ev == sr.SR_EV_VERIFY: return "verify" elif ev == sr.SR_EV_APPLY: return "apply" elif ev == sr.SR_EV_ABORT: return "abort" else: return "unknown" # Function to print current configuration state. # It does so by loading all the items of a session and printing them out. def print_current_config(session, module_name): select_xpath = f"/{module_name}:*//*" values = session.get_items(select_xpath) if values is not None: print("========== BEGIN CONFIG ==========") for i in range(values.val_cnt()): print(values.val(i).to_string(), end='') print("=========== END CONFIG ===========") # Function to be called for subscribed client of given session whenever configuration changes. def module_change_cb(sess, module_name, event, private_ctx): try: print("========== Notification " + ev_to_str(event) + " =============================================") if event == sr.SR_EV_APPLY: print_current_config(sess, module_name) print("========== CHANGES: =============================================") change_path = f"/{module_name}:*" it = sess.get_changes_iter(change_path) while True: change = sess.get_change_next(it) if change is None: break print_change(change.oper(), change.old_val(), change.new_val()) print("========== END OF CHANGES =======================================") except Exception as e: print(e) return sr.SR_ERR_OK def main(): # Notable difference between c implementation is using exception mechanism for open handling unexpected events. # Here it is useful because `Connection`, `Session` and `Subscribe` could throw an exception. try: module_name = "ietf-interfaces" if len(sys.argv) > 1: module_name = sys.argv[1] else: print("\nYou can pass the module name to be subscribed as the first argument") print(f"Application will watch for changes in {module_name}") # connect to sysrepo conn = sr.Connection(module_name) # start session sess = sr.Session(conn) # subscribe for changes in running config */ subscribe = sr.Subscribe(sess) subscribe.module_change_subscribe(module_name, module_change_cb) try: print_current_config(sess, module_name) except Exception as e: print(e) print("========== STARTUP CONFIG APPLIED AS RUNNING ==========") sr.global_loop() print("Application exit requested, exiting.") except Exception as e: print(e) if __name__ == '__main__': main()