|
| 1 | +#!/usr/bin/env python |
| 2 | +"""Copyright 2020 Cisco Systems |
| 3 | +All rights reserved. |
| 4 | +
|
| 5 | +Redistribution and use in source and binary forms, with or without |
| 6 | +modification, are permitted provided that the following conditions are |
| 7 | +met: |
| 8 | +
|
| 9 | + * Redistributions of source code must retain the above copyright |
| 10 | + notice, this list of conditions and the following disclaimer. |
| 11 | +
|
| 12 | +The contents of this file are licensed under the Apache License, Version 2.0 |
| 13 | +(the "License"); you may not use this file except in compliance with the |
| 14 | +License. You may obtain a copy of the License at |
| 15 | +
|
| 16 | +http://www.apache.org/licenses/LICENSE-2.0 |
| 17 | +
|
| 18 | +Unless required by applicable law or agreed to in writing, software |
| 19 | +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 20 | +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 21 | +License for the specific language governing permissions and limitations under |
| 22 | +the License. |
| 23 | +""" |
| 24 | + |
| 25 | +"""Custom usage, no wrapper. |
| 26 | +Because we're not using a wrapper, we are going to need to build our own protos. |
| 27 | +""" |
| 28 | + |
| 29 | +import json |
| 30 | +from getpass import getpass |
| 31 | +from cisco_gnmi import ClientBuilder, proto |
| 32 | + |
| 33 | +"""First let's build a Client. We are not going to specify an OS |
| 34 | +name here resulting in just the base Client returned without any OS |
| 35 | +convenience methods. Client does have some level of "convenience" built-in |
| 36 | +insofar as it doesn't take direct <RPC>Requests (SubscribeRequest) etc. |
| 37 | +To directly use the gNMI RPCs access via client.service.<RPC>(). |
| 38 | +So - either: |
| 39 | + * Pass args to the client.<RPC>() methods. |
| 40 | + * Pass full <RPC>Request protos to client.service.<RPC>() |
| 41 | +This code passes args to the client.<RPC>() methods. |
| 42 | +""" |
| 43 | +target = input("Host/Port: ") |
| 44 | +username = input("Username: ") |
| 45 | +password = getpass() |
| 46 | +client = ( |
| 47 | + ClientBuilder(target) |
| 48 | + .set_secure_from_target() |
| 49 | + .set_ssl_target_override() |
| 50 | + .set_call_authentication(username, password) |
| 51 | + .construct() |
| 52 | +) |
| 53 | +"""Capabilities is an easy RPC to test.""" |
| 54 | +input("Press Enter for Capabilities...") |
| 55 | +capabilities = client.capabilities() |
| 56 | +print(capabilities) |
| 57 | +"""Let's build a Get! |
| 58 | +client.get() expects a list of Paths as the primary method of interaction. |
| 59 | +client.parse_xpath_to_gnmi_path is a convenience method to..parse an XPath to a Path. |
| 60 | +Generally OS wrappers will override this function to specialize on origins, etc. |
| 61 | +But we are not using a wrapper, and if using OpenConfig pathing we don't need an origin. |
| 62 | +""" |
| 63 | +input("Press Enter for Get...") |
| 64 | +get_path = client.parse_xpath_to_gnmi_path("/interfaces/interface/state/counters") |
| 65 | +get_response = client.get([get_path], data_type="STATE", encoding="JSON_IETF") |
| 66 | +print(get_response) |
| 67 | +"""Let's build a sampled Subscribe! |
| 68 | +client.subscribe() accepts an iterable of SubscriptionLists |
| 69 | +""" |
| 70 | +input("Press Enter for Subscribe SAMPLE...") |
| 71 | +subscription_list = proto.gnmi_pb2.SubscriptionList() |
| 72 | +subscription_list.mode = proto.gnmi_pb2.SubscriptionList.Mode.Value("STREAM") |
| 73 | +subscription_list.encoding = proto.gnmi_pb2.Encoding.Value("PROTO") |
| 74 | +sampled_subscription = proto.gnmi_pb2.Subscription() |
| 75 | +sampled_subscription.path.CopyFrom( |
| 76 | + client.parse_xpath_to_gnmi_path("/interfaces/interface/state/counters") |
| 77 | +) |
| 78 | +sampled_subscription.mode = proto.gnmi_pb2.SubscriptionMode.Value("SAMPLE") |
| 79 | +sampled_subscription.sample_interval = 10 * int(1e9) |
| 80 | +subscription_list.subscription.extend([sampled_subscription]) |
| 81 | +for subscribe_response in client.subscribe([subscription_list]): |
| 82 | + print(subscribe_response) |
| 83 | + break |
| 84 | +"""Now let's do ON_CHANGE. Just have to put SubscriptionMode to ON_CHANGE.""" |
| 85 | +input("Press Enter for Subscribe ON_CHANGE...") |
| 86 | +subscription_list = proto.gnmi_pb2.SubscriptionList() |
| 87 | +subscription_list.mode = proto.gnmi_pb2.SubscriptionList.Mode.Value("STREAM") |
| 88 | +subscription_list.encoding = proto.gnmi_pb2.Encoding.Value("PROTO") |
| 89 | +onchange_subscription = proto.gnmi_pb2.Subscription() |
| 90 | +onchange_subscription.path.CopyFrom( |
| 91 | + client.parse_xpath_to_gnmi_path( |
| 92 | + "/syslog/messages/message", origin="Cisco-IOS-XR-infra-syslog-oper" |
| 93 | + ) |
| 94 | +) |
| 95 | +onchange_subscription.mode = proto.gnmi_pb2.SubscriptionMode.Value("ON_CHANGE") |
| 96 | +subscription_list.subscription.extend([onchange_subscription]) |
| 97 | +synced = False |
| 98 | +for subscribe_response in client.subscribe([subscription_list]): |
| 99 | + if subscribe_response.sync_response: |
| 100 | + synced = True |
| 101 | + print("Synced. Now perform action that will create a changed value.") |
| 102 | + print("If using XR syslog as written, just try SSH'ing to device.") |
| 103 | + continue |
| 104 | + if not synced: |
| 105 | + continue |
| 106 | + print(subscribe_response) |
| 107 | + break |
| 108 | +"""Let's build a Set! |
| 109 | +client.set() expects updates, replaces, and/or deletes to be provided. |
| 110 | +updates is a list of Updates |
| 111 | +replaces is a list of Updates |
| 112 | +deletes is a list of Paths |
| 113 | +Let's do an update. |
| 114 | +""" |
| 115 | +input("Press Enter for Set update...") |
| 116 | +set_update = proto.gnmi_pb2.Update() |
| 117 | +# This is the fully modeled JSON we want to update with |
| 118 | +update_json = json.loads( |
| 119 | + """ |
| 120 | +{ |
| 121 | + "openconfig-interfaces:interfaces": { |
| 122 | + "interface": [ |
| 123 | + { |
| 124 | + "name": "Loopback9339" |
| 125 | + } |
| 126 | + ] |
| 127 | + } |
| 128 | +} |
| 129 | +""" |
| 130 | +) |
| 131 | +# Let's just do an update from the very top element |
| 132 | +top_element = next(iter(update_json.keys())) |
| 133 | +set_update.path.CopyFrom(client.parse_xpath_to_gnmi_path(top_element)) |
| 134 | +# Remove the top element from the config since it's now in Path |
| 135 | +update_json = update_json.pop(top_element) |
| 136 | +# Set our update payload |
| 137 | +set_update.val.json_ietf_val = json.dumps(update_json).encode("utf-8") |
| 138 | +set_result = client.set(updates=[set_update]) |
| 139 | +print(set_result) |
| 140 | +# This may all seem somewhat obtuse, and that's what the client wrappers are for. |
0 commit comments