|
| 1 | +"""Copyright 2019 Cisco Systems |
| 2 | +All rights reserved. |
| 3 | +
|
| 4 | +Redistribution and use in source and binary forms, with or without |
| 5 | +modification, are permitted provided that the following conditions are |
| 6 | +met: |
| 7 | +
|
| 8 | + * Redistributions of source code must retain the above copyright |
| 9 | + notice, this list of conditions and the following disclaimer. |
| 10 | +
|
| 11 | +The contents of this file are licensed under the Apache License, Version 2.0 |
| 12 | +(the "License"); you may not use this file except in compliance with the |
| 13 | +License. You may obtain a copy of the License at |
| 14 | +
|
| 15 | +http://www.apache.org/licenses/LICENSE-2.0 |
| 16 | +
|
| 17 | +Unless required by applicable law or agreed to in writing, software |
| 18 | +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 19 | +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 20 | +License for the specific language governing permissions and limitations under |
| 21 | +the License. |
| 22 | +""" |
| 23 | + |
| 24 | +"""Wrapper for NX-OS to simplify usage of gNMI implementation.""" |
| 25 | + |
| 26 | + |
| 27 | +import logging |
| 28 | + |
| 29 | +from six import string_types |
| 30 | +from .client import Client, proto, util |
| 31 | + |
| 32 | + |
| 33 | +class NXClient(Client): |
| 34 | + """NX-OS-specific wrapper for gNMI functionality. |
| 35 | +
|
| 36 | + Returns direct responses from base Client methods. |
| 37 | +
|
| 38 | + Methods |
| 39 | + ------- |
| 40 | + subscribe_xpaths(...) |
| 41 | + Convenience wrapper for subscribe() which helps construct subscriptions for specified xpaths. |
| 42 | +
|
| 43 | + Examples |
| 44 | + -------- |
| 45 | + >>> from cisco_gnmi import ClientBuilder |
| 46 | + >>> client = ClientBuilder('127.0.0.1:9339').set_os( |
| 47 | + ... 'NX-OS' |
| 48 | + ... ).set_secure_from_file().set_ssl_target_override().set_call_authentication( |
| 49 | + ... 'admin', |
| 50 | + ... 'its_a_secret' |
| 51 | + ... ).construct() |
| 52 | + >>> capabilities = client.capabilities() |
| 53 | + >>> print(capabilities) |
| 54 | + """ |
| 55 | + |
| 56 | + def get(self, *args, **kwargs): |
| 57 | + raise NotImplementedError("Get not yet supported on NX-OS!") |
| 58 | + |
| 59 | + def set(self, *args, **kwargs): |
| 60 | + raise NotImplementedError("Set not yet supported on NX-OS!") |
| 61 | + |
| 62 | + def subscribe_xpaths( |
| 63 | + self, |
| 64 | + xpath_subscriptions, |
| 65 | + request_mode="STREAM", |
| 66 | + sub_mode="SAMPLE", |
| 67 | + encoding="PROTO", |
| 68 | + sample_interval=Client._NS_IN_S * 10, |
| 69 | + ): |
| 70 | + """A convenience wrapper of subscribe() which aids in building of SubscriptionRequest |
| 71 | + with request as subscribe SubscriptionList. This method accepts an iterable of simply xpath strings, |
| 72 | + dictionaries with Subscription attributes for more granularity, or already built Subscription |
| 73 | + objects and builds the SubscriptionList. Fields not supplied will be defaulted with the default arguments |
| 74 | + to the method. |
| 75 | +
|
| 76 | + Generates a single SubscribeRequest. |
| 77 | +
|
| 78 | + Parameters |
| 79 | + ---------- |
| 80 | + xpath_subscriptions : str or iterable of str, dict, Subscription |
| 81 | + An iterable which is parsed to form the Subscriptions in the SubscriptionList to be passed |
| 82 | + to SubscriptionRequest. Strings are parsed as XPaths and defaulted with the default arguments, |
| 83 | + dictionaries are treated as dicts of args to pass to the Subscribe init, and Subscription is |
| 84 | + treated as simply a pre-made Subscription. |
| 85 | + request_mode : proto.gnmi_pb2.SubscriptionList.Mode, optional |
| 86 | + Indicates whether STREAM to stream from target, |
| 87 | + ONCE to stream once (like a get), |
| 88 | + POLL to respond to POLL. |
| 89 | + [STREAM, ONCE, POLL] |
| 90 | + sub_mode : proto.gnmi_pb2.SubscriptionMode, optional |
| 91 | + The default SubscriptionMode on a per Subscription basis in the SubscriptionList. |
| 92 | + ON_CHANGE only streams updates when changes occur. |
| 93 | + SAMPLE will stream the subscription at a regular cadence/interval. |
| 94 | + [ON_CHANGE, SAMPLE] |
| 95 | + encoding : proto.gnmi_pb2.Encoding, optional |
| 96 | + A member of the proto.gnmi_pb2.Encoding enum specifying desired encoding of returned data |
| 97 | + [JSON, PROTO] |
| 98 | + sample_interval : int, optional |
| 99 | + Default nanoseconds for sample to occur. |
| 100 | + Defaults to 10 seconds. |
| 101 | +
|
| 102 | + Returns |
| 103 | + ------- |
| 104 | + subscribe() |
| 105 | + """ |
| 106 | + supported_request_modes = ["STREAM", "ONCE", "POLL"] |
| 107 | + supported_encodings = ["JSON", "PROTO"] |
| 108 | + supported_sub_modes = ["ON_CHANGE", "SAMPLE"] |
| 109 | + subscription_list = proto.gnmi_pb2.SubscriptionList() |
| 110 | + subscription_list.mode = util.validate_proto_enum( |
| 111 | + "mode", |
| 112 | + request_mode, |
| 113 | + "SubscriptionList.Mode", |
| 114 | + proto.gnmi_pb2.SubscriptionList.Mode, |
| 115 | + supported_request_modes, |
| 116 | + ) |
| 117 | + subscription_list.encoding = util.validate_proto_enum( |
| 118 | + "encoding", |
| 119 | + encoding, |
| 120 | + "Encoding", |
| 121 | + proto.gnmi_pb2.Encoding, |
| 122 | + supported_encodings, |
| 123 | + ) |
| 124 | + if isinstance(xpath_subscriptions, string_types): |
| 125 | + xpath_subscriptions = [xpath_subscriptions] |
| 126 | + for xpath_subscription in xpath_subscriptions: |
| 127 | + subscription = None |
| 128 | + if isinstance(xpath_subscription, string_types): |
| 129 | + subscription = proto.gnmi_pb2.Subscription() |
| 130 | + subscription.path.CopyFrom( |
| 131 | + self.parse_xpath_to_gnmi_path(xpath_subscription) |
| 132 | + ) |
| 133 | + subscription.mode = util.validate_proto_enum( |
| 134 | + "sub_mode", |
| 135 | + sub_mode, |
| 136 | + "SubscriptionMode", |
| 137 | + proto.gnmi_pb2.SubscriptionMode, |
| 138 | + supported_sub_modes, |
| 139 | + ) |
| 140 | + subscription.sample_interval = sample_interval |
| 141 | + elif isinstance(xpath_subscription, dict): |
| 142 | + path = self.parse_xpath_to_gnmi_path(xpath_subscription["path"]) |
| 143 | + arg_dict = { |
| 144 | + "path": path, |
| 145 | + "mode": sub_mode, |
| 146 | + "sample_interval": sample_interval, |
| 147 | + } |
| 148 | + arg_dict.update(xpath_subscription) |
| 149 | + if "mode" in arg_dict: |
| 150 | + arg_dict["mode"] = util.validate_proto_enum( |
| 151 | + "sub_mode", |
| 152 | + arg_dict["mode"], |
| 153 | + "SubscriptionMode", |
| 154 | + proto.gnmi_pb2.SubscriptionMode, |
| 155 | + supported_sub_modes, |
| 156 | + ) |
| 157 | + subscription = proto.gnmi_pb2.Subscription(**arg_dict) |
| 158 | + elif isinstance(xpath_subscription, proto.gnmi_pb2.Subscription): |
| 159 | + subscription = xpath_subscription |
| 160 | + else: |
| 161 | + raise Exception("xpath in list must be xpath or dict/Path!") |
| 162 | + subscription_list.subscription.append(subscription) |
| 163 | + return self.subscribe([subscription_list]) |
| 164 | + |
| 165 | + def parse_xpath_to_gnmi_path(self, xpath, origin=None): |
| 166 | + """Origin defaults to YANG (device) paths |
| 167 | + Otherwise specify "DME" as origin |
| 168 | + """ |
| 169 | + if xpath.startswith("openconfig"): |
| 170 | + raise NotImplementedError( |
| 171 | + "OpenConfig data models not yet supported on NX-OS!" |
| 172 | + ) |
| 173 | + if origin is None: |
| 174 | + if any(map(xpath.startswith, ["Cisco-NX-OS-device", "ietf-interfaces"])): |
| 175 | + origin = "device" |
| 176 | + else: |
| 177 | + origin = "DME" |
| 178 | + return super(NXClient, self).parse_xpath_to_gnmi_path(xpath, origin) |
0 commit comments