25
25
26
26
import json
27
27
import logging
28
+ import os
28
29
29
30
from six import string_types
30
31
from .client import Client , proto , util
@@ -181,7 +182,66 @@ def delete_xpaths(self, xpaths, prefix=None):
181
182
paths .append (self .parse_xpath_to_gnmi_path (xpath ))
182
183
return self .set (deletes = paths )
183
184
184
- def set_json (self , update_json_configs = None , replace_json_configs = None , ietf = True ):
185
+ def check_configs (self , configs ):
186
+ if isinstance (configs , string_types ):
187
+ logger .debug ("Handling as JSON string." )
188
+ try :
189
+ configs = json .loads (configs )
190
+ except :
191
+ raise Exception ("{0}\n is invalid JSON!" .format (configs ))
192
+ configs = [configs ]
193
+ elif isinstance (configs , dict ):
194
+ logger .debug ("Handling already serialized JSON object." )
195
+ configs = [configs ]
196
+ elif not isinstance (configs , (list , set )):
197
+ raise Exception (
198
+ "{0} must be an iterable of configs!" .format (str (configs ))
199
+ )
200
+ return configs
201
+
202
+ def create_updates (self , configs , origin , json_ietf = True ):
203
+ if not configs :
204
+ return None
205
+ configs = self .check_configs (configs )
206
+
207
+ xpaths = []
208
+ updates = []
209
+ for config in configs :
210
+ xpath = next (iter (config .keys ()))
211
+ xpaths .append (xpath )
212
+ common_xpath = os .path .commonprefix (xpaths )
213
+
214
+ if common_xpath :
215
+ update_configs = self .get_payload (configs )
216
+ for update_cfg in update_configs :
217
+ xpath , payload = update_cfg
218
+ update = proto .gnmi_pb2 .Update ()
219
+ update .path .CopyFrom (
220
+ self .parse_xpath_to_gnmi_path (
221
+ xpath , origin = origin
222
+ )
223
+ )
224
+ if json_ietf :
225
+ update .val .json_ietf_val = payload
226
+ else :
227
+ update .val .json_val = payload
228
+ updates .append (update )
229
+ return updates
230
+ else :
231
+ for config in configs :
232
+ top_element = next (iter (config .keys ()))
233
+ update = proto .gnmi_pb2 .Update ()
234
+ update .path .CopyFrom (self .parse_xpath_to_gnmi_path (top_element ))
235
+ config = config .pop (top_element )
236
+ if json_ietf :
237
+ update .val .json_ietf_val = json .dumps (config ).encode ("utf-8" )
238
+ else :
239
+ update .val .json_val = json .dumps (config ).encode ("utf-8" )
240
+ updates .append (update )
241
+ return updates
242
+
243
+ def set_json (self , update_json_configs = None , replace_json_configs = None ,
244
+ origin = 'device' , json_ietf = True ):
185
245
"""A convenience wrapper for set() which assumes JSON payloads and constructs desired messages.
186
246
All parameters are optional, but at least one must be present.
187
247
@@ -194,8 +254,7 @@ def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=Tru
194
254
JSON configs to apply as updates.
195
255
replace_json_configs : iterable of JSON configurations, optional
196
256
JSON configs to apply as replacements.
197
- ietf : bool, optional
198
- Use JSON_IETF vs JSON.
257
+ origin : openconfig, device, or DME
199
258
200
259
Returns
201
260
-------
@@ -204,52 +263,19 @@ def set_json(self, update_json_configs=None, replace_json_configs=None, ietf=Tru
204
263
if not any ([update_json_configs , replace_json_configs ]):
205
264
raise Exception ("Must supply at least one set of configurations to method!" )
206
265
207
- def check_configs (configs ):
208
- if isinstance (configs , string_types ):
209
- logger .debug ("Handling as JSON string." )
210
- try :
211
- configs = json .loads (configs )
212
- except :
213
- raise Exception ("{0}\n is invalid JSON!" .format (configs ))
214
- configs = [configs ]
215
- elif isinstance (configs , dict ):
216
- logger .debug ("Handling already serialized JSON object." )
217
- configs = [configs ]
218
- elif not isinstance (configs , (list , set )):
219
- raise Exception (
220
- "{0} must be an iterable of configs!" .format (str (configs ))
221
- )
222
- return configs
223
-
224
- def create_updates (configs ):
225
- if not configs :
226
- return None
227
- configs = check_configs (configs )
228
- updates = []
229
- for config in configs :
230
- if not isinstance (config , dict ):
231
- raise Exception ("config must be a JSON object!" )
232
- if len (config .keys ()) > 1 :
233
- raise Exception ("config should only target one YANG module!" )
234
- top_element = next (iter (config .keys ()))
235
- # start mike
236
- # path_obj = self.parse_xpath_to_gnmi_path(top_element)
237
- # config = config.pop(top_element)
238
- # value_obj = proto.gnmi_pb2.TypedValue(json_ietf_val=json.dumps(config).encode("utf-8"))
239
- # update = proto.gnmi_pb2.Update(path=path_obj, val=value_obj)
240
- # end mike
241
- update = proto .gnmi_pb2 .Update ()
242
- update .path .CopyFrom (self .parse_xpath_to_gnmi_path (top_element ))
243
- config = config .pop (top_element )
244
- if ietf :
245
- update .val .json_ietf_val = json .dumps (config ).encode ("utf-8" )
246
- else :
247
- update .val .json_val = json .dumps (config ).encode ("utf-8" )
248
- updates .append (update )
249
- return updates
266
+ updates = self .create_updates (
267
+ update_json_configs ,
268
+ origin = origin ,
269
+ json_ietf = json_ietf
270
+ )
271
+ replaces = self .create_updates (
272
+ replace_json_configs ,
273
+ origin = origin ,
274
+ json_ietf = json_ietf
275
+ )
276
+ for update in updates + replaces :
277
+ logger .info ('\n GNMI set:\n {0}\n {1}' .format (9 * '=' , str (update )))
250
278
251
- updates = create_updates (update_json_configs )
252
- replaces = create_updates (replace_json_configs )
253
279
return self .set (updates = updates , replaces = replaces )
254
280
255
281
def get_xpaths (self , xpaths , data_type = "ALL" , encoding = "JSON_IETF" , origin = None ):
@@ -296,9 +322,11 @@ def get_xpaths(self, xpaths, data_type="ALL", encoding="JSON_IETF", origin=None)
296
322
def subscribe_xpaths (
297
323
self ,
298
324
xpath_subscriptions ,
299
- encoding = "JSON_IETF" ,
325
+ request_mode = "STREAM" ,
326
+ sub_mode = "SAMPLE" ,
327
+ encoding = "PROTO" ,
300
328
sample_interval = Client ._NS_IN_S * 10 ,
301
- heartbeat_interval = None ,
329
+ origin = 'openconfig'
302
330
):
303
331
"""A convenience wrapper of subscribe() which aids in building of SubscriptionRequest
304
332
with request as subscribe SubscriptionList. This method accepts an iterable of simply xpath strings,
@@ -315,26 +343,30 @@ def subscribe_xpaths(
315
343
to SubscriptionRequest. Strings are parsed as XPaths and defaulted with the default arguments,
316
344
dictionaries are treated as dicts of args to pass to the Subscribe init, and Subscription is
317
345
treated as simply a pre-made Subscription.
346
+ request_mode : proto.gnmi_pb2.SubscriptionList.Mode, optional
347
+ Indicates whether STREAM to stream from target,
348
+ ONCE to stream once (like a get),
349
+ POLL to respond to POLL.
350
+ [STREAM, ONCE, POLL]
351
+ sub_mode : proto.gnmi_pb2.SubscriptionMode, optional
352
+ The default SubscriptionMode on a per Subscription basis in the SubscriptionList.
353
+ ON_CHANGE only streams updates when changes occur.
354
+ SAMPLE will stream the subscription at a regular cadence/interval.
355
+ [ON_CHANGE, SAMPLE]
318
356
encoding : proto.gnmi_pb2.Encoding, optional
319
357
A member of the proto.gnmi_pb2.Encoding enum specifying desired encoding of returned data
320
- [JSON, JSON_IETF ]
358
+ [JSON, PROTO ]
321
359
sample_interval : int, optional
322
360
Default nanoseconds for sample to occur.
323
361
Defaults to 10 seconds.
324
- heartbeat_interval : int, optional
325
- Specifies the maximum allowable silent period in nanoseconds when
326
- suppress_redundant is in use. The target should send a value at least once
327
- in the period specified.
328
362
329
363
Returns
330
364
-------
331
365
subscribe()
332
366
"""
333
- supported_request_modes = ["STREAM" ]
334
- request_mode = "STREAM"
335
- supported_sub_modes = ["SAMPLE" ]
336
- sub_mode = "SAMPLE"
367
+ supported_request_modes = ["STREAM" , "ONCE" , "POLL" ]
337
368
supported_encodings = ["JSON" , "JSON_IETF" ]
369
+ supported_sub_modes = ["ON_CHANGE" , "SAMPLE" ]
338
370
subscription_list = proto .gnmi_pb2 .SubscriptionList ()
339
371
subscription_list .mode = util .validate_proto_enum (
340
372
"mode" ,
@@ -358,7 +390,10 @@ def subscribe_xpaths(
358
390
if isinstance (xpath_subscription , string_types ):
359
391
subscription = proto .gnmi_pb2 .Subscription ()
360
392
subscription .path .CopyFrom (
361
- self .parse_xpath_to_gnmi_path (xpath_subscription )
393
+ self .parse_xpath_to_gnmi_path (
394
+ xpath_subscription ,
395
+ origin
396
+ )
362
397
)
363
398
subscription .mode = util .validate_proto_enum (
364
399
"sub_mode" ,
@@ -369,7 +404,10 @@ def subscribe_xpaths(
369
404
)
370
405
subscription .sample_interval = sample_interval
371
406
elif isinstance (xpath_subscription , dict ):
372
- path = self .parse_xpath_to_gnmi_path (xpath_subscription ["path" ])
407
+ path = self .parse_xpath_to_gnmi_path (
408
+ xpath_subscription ["path" ],
409
+ origin
410
+ )
373
411
arg_dict = {
374
412
"path" : path ,
375
413
"mode" : sub_mode ,
@@ -391,6 +429,9 @@ def subscribe_xpaths(
391
429
raise Exception ("xpath in list must be xpath or dict/Path!" )
392
430
subscriptions .append (subscription )
393
431
subscription_list .subscription .extend (subscriptions )
432
+ logger .info ('GNMI subscribe:\n {0}\n {1}' .format (
433
+ 15 * '=' , str (subscription_list ))
434
+ )
394
435
return self .subscribe ([subscription_list ])
395
436
396
437
def parse_xpath_to_gnmi_path (self , xpath , origin = None ):
0 commit comments