Skip to content

Commit c31369e

Browse files
committed
Add support for get and set in Nexus client.
1 parent 7d1135c commit c31369e

File tree

3 files changed

+143
-4
lines changed

3 files changed

+143
-4
lines changed

src/cisco_gnmi/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def subscribe_xpaths(
272272
sample_interval=_NS_IN_S * 10,
273273
suppress_redundant=False,
274274
heartbeat_interval=None,
275+
prefix=None
275276
):
276277
"""A convenience wrapper of subscribe() which aids in building of SubscriptionRequest
277278
with request as subscribe SubscriptionList. This method accepts an iterable of simply xpath strings,
@@ -330,6 +331,8 @@ def subscribe_xpaths(
330331
subscription_list.encoding = util.validate_proto_enum(
331332
"encoding", encoding, "Encoding", proto.gnmi_pb2.Encoding
332333
)
334+
if prefix:
335+
subscription_list.prefix.CopyFrom(prefix)
333336
if isinstance(
334337
xpath_subscriptions, (string_types, dict, proto.gnmi_pb2.Subscription)
335338
):

src/cisco_gnmi/nx.py

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,146 @@ class NXClient(Client):
5252
>>> capabilities = client.capabilities()
5353
>>> print(capabilities)
5454
"""
55+
def delete_xpaths(self, xpaths, prefix=None):
56+
"""A convenience wrapper for set() which constructs Paths from supplied xpaths
57+
to be passed to set() as the delete parameter.
5558
56-
def get(self, *args, **kwargs):
57-
raise NotImplementedError("Get not yet supported on NX-OS!")
59+
Parameters
60+
----------
61+
xpaths : iterable of str
62+
XPaths to specify to be deleted.
63+
If prefix is specified these strings are assumed to be the suffixes.
64+
prefix : str
65+
The XPath prefix to apply to all XPaths for deletion.
66+
67+
Returns
68+
-------
69+
set()
70+
"""
71+
if isinstance(xpaths, string_types):
72+
xpaths = [xpaths]
73+
paths = []
74+
# prefix is not supported on NX yet
75+
prefix = None
76+
for xpath in xpaths:
77+
if prefix:
78+
if prefix.endswith("/") and xpath.startswith("/"):
79+
xpath = "{prefix}{xpath}".format(
80+
prefix=prefix[:-1], xpath=xpath[1:]
81+
)
82+
elif prefix.endswith("/") or xpath.startswith("/"):
83+
xpath = "{prefix}{xpath}".format(prefix=prefix, xpath=xpath)
84+
else:
85+
xpath = "{prefix}/{xpath}".format(prefix=prefix, xpath=xpath)
86+
paths.append(self.parse_xpath_to_gnmi_path(xpath))
87+
return self.set(deletes=paths)
88+
89+
def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=False, prefix=None):
90+
"""A convenience wrapper for set() which assumes JSON payloads and constructs desired messages.
91+
All parameters are optional, but at least one must be present.
92+
93+
This method expects JSON in the same format as what you might send via the native gRPC interface
94+
with a fully modeled configuration which is then parsed to meet the gNMI implementation.
95+
96+
Parameters
97+
----------
98+
update_json_configs : iterable of JSON configurations, optional
99+
JSON configs to apply as updates.
100+
replace_json_configs : iterable of JSON configurations, optional
101+
JSON configs to apply as replacements.
102+
ietf : bool, optional
103+
Use JSON_IETF vs JSON.
104+
105+
Returns
106+
-------
107+
set()
108+
"""
109+
# JSON_IETF and prefix are not supported on NX yet
110+
ietf = False
111+
prefix = None
112+
113+
if not any([update_json_configs, replace_json_configs]):
114+
raise Exception("Must supply at least one set of configurations to method!")
115+
116+
def check_configs(name, configs):
117+
if isinstance(configs, string_types):
118+
logger.debug("Handling %s as JSON string.", name)
119+
try:
120+
configs = json.loads(configs)
121+
except:
122+
raise Exception("{name} is invalid JSON!".format(name=name))
123+
configs = [configs]
124+
elif isinstance(configs, dict):
125+
logger.debug("Handling %s as already serialized JSON object.", name)
126+
configs = [configs]
127+
elif not isinstance(configs, (list, set)):
128+
raise Exception(
129+
"{name} must be an iterable of configs!".format(name=name)
130+
)
131+
return configs
58132

59-
def set(self, *args, **kwargs):
60-
raise NotImplementedError("Set not yet supported on NX-OS!")
133+
def create_updates(name, configs):
134+
if not configs:
135+
return None
136+
configs = check_configs(name, configs)
137+
updates = []
138+
for config in configs:
139+
if not isinstance(config, dict):
140+
raise Exception("config must be a JSON object!")
141+
if len(config.keys()) > 1:
142+
raise Exception("config should only target one YANG module!")
143+
top_element = next(iter(config.keys()))
144+
update = proto.gnmi_pb2.Update()
145+
update.path.CopyFrom(self.parse_xpath_to_gnmi_path(top_element))
146+
config = config.pop(top_element)
147+
if ietf:
148+
update.val.json_ietf_val = json.dumps(config).encode("utf-8")
149+
else:
150+
update.val.json_val = json.dumps(config).encode("utf-8")
151+
updates.append(update)
152+
return updates
153+
154+
updates = create_updates("update_json_configs", update_json_configs)
155+
replaces = create_updates("replace_json_configs", replace_json_configs)
156+
return self.set(prefix=prefix, updates=updates, replaces=replaces)
157+
158+
def get_xpaths(self, xpaths, data_type="ALL", encoding="JSON_IETF"):
159+
"""A convenience wrapper for get() which forms proto.gnmi_pb2.Path from supplied xpaths.
160+
161+
Parameters
162+
----------
163+
xpaths : iterable of str or str
164+
An iterable of XPath strings to request data of
165+
If simply a str, wraps as a list for convenience
166+
data_type : proto.gnmi_pb2.GetRequest.DataType, optional
167+
A direct value or key from the GetRequest.DataType enum
168+
[ALL, CONFIG, STATE, OPERATIONAL]
169+
encoding : proto.gnmi_pb2.GetRequest.Encoding, optional
170+
A direct value or key from the Encoding enum
171+
[JSON, JSON_IETF]
172+
173+
Returns
174+
-------
175+
get()
176+
"""
177+
supported_encodings = ["JSON", "JSON_IETF"]
178+
encoding = util.validate_proto_enum(
179+
"encoding",
180+
encoding,
181+
"Encoding",
182+
proto.gnmi_pb2.Encoding,
183+
supported_encodings,
184+
)
185+
gnmi_path = None
186+
if isinstance(xpaths, (list, set)):
187+
gnmi_path = map(self.parse_xpath_to_gnmi_path, set(xpaths))
188+
elif isinstance(xpaths, string_types):
189+
gnmi_path = [self.parse_xpath_to_gnmi_path(xpaths)]
190+
else:
191+
raise Exception(
192+
"xpaths must be a single xpath string or iterable of xpath strings!"
193+
)
194+
return self.get(gnmi_path, data_type=data_type, encoding=encoding)
61195

62196
def subscribe_xpaths(
63197
self,

src/cisco_gnmi/xe.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ def subscribe_xpaths(
223223
sample_interval=Client._NS_IN_S * 10,
224224
suppress_redundant=False,
225225
heartbeat_interval=None,
226+
prefix=None
226227
):
227228
"""A convenience wrapper of subscribe() which aids in building of SubscriptionRequest
228229
with request as subscribe SubscriptionList. This method accepts an iterable of simply xpath strings,
@@ -298,6 +299,7 @@ def subscribe_xpaths(
298299
sample_interval,
299300
suppress_redundant,
300301
heartbeat_interval,
302+
prefix
301303
)
302304

303305
def parse_xpath_to_gnmi_path(self, xpath, origin=None):

0 commit comments

Comments
 (0)