Skip to content

Commit 3e006c3

Browse files
authored
Merge pull request #93 from akarneliuk/0.8.8
0.8.8
2 parents 33893c7 + 8fc8b3a commit 3e006c3

File tree

6 files changed

+99
-90
lines changed

6 files changed

+99
-90
lines changed

README.rst

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ Contributors
8686
Dev Log
8787
=======
8888

89+
Release **0.8.8**:
90+
91+
- Added new argument ``-e / --encoding`` to ``pygnmicli`` to specify the encoding, which overrides the default one. Fix for `Issue 58 <https://github.com/akarneliuk/pygnmi/issues/58>`_.
92+
- Fixed minor bug with encoding handling inside ``get()`` and ``subscribe2()`` methods.
93+
- Simplified the code.
94+
8995
Release **0.8.7**:
9096

9197
- Fixed bug, when returned ``json_val`` or ``json_ietf_val`` is not processed correctly if the value is empty string. Fix for `Issue 58 <https://github.com/akarneliuk/pygnmi/issues/58>`_.

pygnmi/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
pyGNMI module to manage network devices with gNMI
33
(c)2020-2022, Karneliuk
44
"""
5-
__version__ = "0.8.7"
5+
__version__ = "0.8.8"

pygnmi/arg_parser.py

+8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ def parse_args(msg):
8888
default="capabilities",
8989
help="gNMI Request type",
9090
)
91+
parser.add_argument(
92+
"-e", "--encoding",
93+
type=str,
94+
required=False,
95+
choices=["json", "bytes", "proto", "ascii", "json_ietf"],
96+
default="json",
97+
help="Specif the encoding in the gNMI RPC (subject to be supported by the network device",
98+
)
9199
parser.add_argument(
92100
"-x", "--gnmi-path",
93101
type=str,

pygnmi/client.py

+77-84
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ def get(self, prefix: str = "", path: list = None,
338338

339339
datatype = datatype.lower()
340340
type_dict = {'all', 'config', 'state', 'operational'}
341-
encoding_set = {'json', 'bytes', 'proto', 'ascii', 'json_ietf'}
342341

343342
# Set Protobuf value for information type
344343
pb_datatype = 0
@@ -355,20 +354,9 @@ def get(self, prefix: str = "", path: list = None,
355354
logger.error('The GetRequst data type is not within the defined range. Using default type \'all\'.')
356355

357356
# Set Protobuf value for encoding
358-
pb_encoding = 4
359-
if encoding in encoding_set:
360-
if encoding.lower() == 'json':
361-
pb_encoding = 0
362-
elif encoding.lower() == 'bytes':
363-
pb_encoding = 1
364-
elif encoding.lower() == 'proto':
365-
pb_encoding = 2
366-
elif encoding.lower() == 'ascii':
367-
pb_encoding = 3
368-
else:
369-
pb_encoding = 4
370-
else:
371-
logger.error('The GetRequst encoding is not within the defined range. Using default type \'json_ietf\'.')
357+
pb_encoding = choose_encoding(collected_capabilities=self.__capabilities,
358+
default_encoding="json",
359+
requested_encoding=encoding)
372360

373361
# Gnmi PREFIX
374362
try:
@@ -390,12 +378,6 @@ def get(self, prefix: str = "", path: list = None,
390378
logger.error('Conversion of gNMI paths to the Protobuf format failed')
391379
raise gNMIException('Conversion of gNMI paths to the Protobuf format failed', e)
392380

393-
if self.__capabilities and 'supported_encodings' in self.__capabilities:
394-
if 'json' in self.__capabilities['supported_encodings']:
395-
pb_encoding = 0
396-
elif 'json_ietf' in self.__capabilities['supported_encodings']:
397-
pb_encoding = 4
398-
399381
try:
400382
gnmi_message_request = GetRequest(prefix=protobuf_prefix, path=protobuf_paths,
401383
type=pb_datatype, encoding=pb_encoding)
@@ -513,12 +495,11 @@ def set(self, delete: list = None, replace: list = None,
513495
del_protobuf_paths = []
514496
replace_msg = []
515497
update_msg = []
516-
encoding_set = {'json', 'bytes', 'proto', 'ascii', 'json_ietf'}
517498
diff_list = []
518499

519-
if encoding not in encoding_set:
520-
logger.error(f'The encoding {encoding} is not supported. The allowed are: {", ".join(encoding_set)}.')
521-
raise gNMIException(f'The encoding {encoding} is not supported. The allowed are: {", ".join(encoding_set)}.')
500+
if encoding.upper() not in Encoding.keys():
501+
logger.error(f'The encoding {encoding} is not supported. The allowed are: {", ".join(Encoding.keys())}.')
502+
raise gNMIException(f'The encoding {encoding} is not supported. The allowed are: {", ".join(Encoding.keys())}.')
522503

523504
# Gnmi PREFIX
524505
try:
@@ -543,57 +524,11 @@ def set(self, delete: list = None, replace: list = None,
543524

544525
# Replace operation
545526
if replace:
546-
if isinstance(replace, list):
547-
for ue in replace:
548-
if isinstance(ue, tuple):
549-
u_path = gnmi_path_generator(ue[0])
550-
u_val = json.dumps(ue[1]).encode('utf-8')
551-
552-
if encoding == 'json':
553-
replace_msg.append(Update(path=u_path, val=TypedValue(json_val=u_val)))
554-
elif encoding == 'bytes':
555-
replace_msg.append(Update(path=u_path, val=TypedValue(bytes_val=u_val)))
556-
elif encoding == 'proto':
557-
replace_msg.append(Update(path=u_path, val=TypedValue(proto_bytes=u_val)))
558-
elif encoding == 'ascii':
559-
replace_msg.append(Update(path=u_path, val=TypedValue(ascii_val=u_val[1:-1])))
560-
elif encoding == 'json_ietf':
561-
replace_msg.append(Update(path=u_path, val=TypedValue(json_ietf_val=u_val)))
562-
563-
else:
564-
logger.error(f'The input element for Update message must be tuple, got {ue}.')
565-
raise gNMIException(f'The input element for Update message must be tuple, got {ue}.')
566-
567-
else:
568-
logger.error(f'The provided input for Set message (replace operation) is not list.')
569-
raise gNMIException('The provided input for Set message (replace operation) is not list.')
527+
replace_msg = construct_update_message(user_list=replace, encoding=encoding)
570528

571529
# Update operation
572530
if update:
573-
if isinstance(update, list):
574-
for ue in update:
575-
if isinstance(ue, tuple):
576-
u_path = gnmi_path_generator(ue[0])
577-
u_val = json.dumps(ue[1]).encode('utf-8')
578-
579-
if encoding == 'json':
580-
update_msg.append(Update(path=u_path, val=TypedValue(json_val=u_val)))
581-
elif encoding == 'bytes':
582-
update_msg.append(Update(path=u_path, val=TypedValue(bytes_val=u_val)))
583-
elif encoding == 'proto':
584-
update_msg.append(Update(path=u_path, val=TypedValue(proto_bytes=u_val)))
585-
elif encoding == 'ascii':
586-
update_msg.append(Update(path=u_path, val=TypedValue(ascii_val=u_val[1:-1])))
587-
elif encoding == 'json_ietf':
588-
update_msg.append(Update(path=u_path, val=TypedValue(json_ietf_val=u_val)))
589-
590-
else:
591-
logger.error(f'The input element for Update message must be tuple, got {ue}.')
592-
raise gNMIException(f'The input element for Update message must be tuple, got {ue}.')
593-
594-
else:
595-
logger.error(f'The provided input for Set message (update operation) is not list.')
596-
raise gNMIException('The provided input for Set message (update operation) is not list.')
531+
update_msg = construct_update_message(user_list=update, encoding=encoding)
597532

598533
try:
599534
# Adding collection of data for diff before the change
@@ -739,8 +674,9 @@ def _build_subscriptionrequest(self, subscribe: dict, target: str = None, extens
739674
if 'encoding' not in subscribe:
740675
subscribe.update({'encoding': 'proto'})
741676

742-
if subscribe['encoding'].upper() not in Encoding.keys():
743-
raise ValueError(f'Subscribe encoding {subscribe["encoding"]} is out of allowed ranges.')
677+
pb_encoding = choose_encoding(collected_capabilities=self.__capabilities,
678+
default_encoding="proto",
679+
requested_encoding=subscribe['encoding'])
744680

745681
# qos
746682
if 'qos' not in subscribe or not subscribe["qos"]:
@@ -766,13 +702,13 @@ def _build_subscriptionrequest(self, subscribe: dict, target: str = None, extens
766702

767703
# Create message for eveyrhting besides subscriptions
768704
request = SubscriptionList(prefix=gnmi_path_generator(subscribe['prefix'], target),
769-
use_aliases=subscribe['use_aliases'],
770-
qos=subscribe['qos'],
771-
mode=subscribe_mode,
772-
allow_aggregation=subscribe['allow_aggregation'],
773-
use_models=subscribe['use_models'],
774-
encoding=Encoding.Value(subscribe['encoding'].upper()),
775-
updates_only=subscribe['updates_only'])
705+
use_aliases=subscribe['use_aliases'],
706+
qos=subscribe['qos'],
707+
mode=subscribe_mode,
708+
allow_aggregation=subscribe['allow_aggregation'],
709+
use_models=subscribe['use_models'],
710+
encoding=pb_encoding,
711+
updates_only=subscribe['updates_only'])
776712

777713
# subscription
778714
if 'subscription' not in subscribe or not subscribe['subscription']:
@@ -1242,7 +1178,9 @@ def telemetryParser(in_message=None, debug: bool = False):
12421178
return None
12431179

12441180

1245-
def debug_gnmi_msg(is_printable, what_to_print, message_name) -> None:
1181+
def debug_gnmi_msg(is_printable: bool,
1182+
what_to_print: str,
1183+
message_name: str) -> None:
12461184
"""This helper function prints debug output"""
12471185
if is_printable:
12481186
print(message_name)
@@ -1252,7 +1190,7 @@ def debug_gnmi_msg(is_printable, what_to_print, message_name) -> None:
12521190

12531191

12541192
def process_potentially_json_value(input_val) -> Any:
1255-
"""This helper method converts value from bytestream"""
1193+
"""This helper function converts value from bytestream"""
12561194
unprocessed_value = input_val.decode(encoding="utf-8")
12571195

12581196
if unprocessed_value:
@@ -1264,3 +1202,58 @@ def process_potentially_json_value(input_val) -> Any:
12641202
processed_val = None
12651203

12661204
return processed_val
1205+
1206+
1207+
def choose_encoding(collected_capabilities: list,
1208+
default_encoding: str,
1209+
requested_encoding: str = None) -> int:
1210+
"""This helper function chooses the needed encoding"""
1211+
# Default encoding equals to JSON_IETF in Protobuf format
1212+
result = 4
1213+
1214+
if requested_encoding:
1215+
if requested_encoding.upper() in Encoding.keys():
1216+
result = Encoding.Value(name=requested_encoding.upper())
1217+
1218+
else:
1219+
raise ValueError(f'Subscribe encoding {requested_encoding} is out of allowed ranges.')
1220+
1221+
else:
1222+
if collected_capabilities and 'supported_encodings' in collected_capabilities and\
1223+
default_encoding in collected_capabilities['supported_encodings']:
1224+
result = Encoding.Value(name=default_encoding.upper())
1225+
1226+
return result
1227+
1228+
1229+
def construct_update_message(user_list: list,
1230+
encoding: str) -> list:
1231+
"""This is a helper method to construct the Update() GNMI message"""
1232+
result = []
1233+
1234+
if isinstance(user_list, list):
1235+
for ue in user_list:
1236+
if isinstance(ue, tuple):
1237+
u_path = gnmi_path_generator(ue[0])
1238+
u_val = json.dumps(ue[1]).encode('utf-8')
1239+
1240+
if encoding == 'json':
1241+
result.append(Update(path=u_path, val=TypedValue(json_val=u_val)))
1242+
elif encoding == 'bytes':
1243+
result.append(Update(path=u_path, val=TypedValue(bytes_val=u_val)))
1244+
elif encoding == 'proto':
1245+
result.append(Update(path=u_path, val=TypedValue(proto_bytes=u_val)))
1246+
elif encoding == 'ascii':
1247+
result.append(Update(path=u_path, val=TypedValue(ascii_val=u_val[1:-1])))
1248+
elif encoding == 'json_ietf':
1249+
result.append(Update(path=u_path, val=TypedValue(json_ietf_val=u_val)))
1250+
1251+
else:
1252+
logger.error(f'The input element for Update message must be tuple, got {ue}.')
1253+
raise gNMIException(f'The input element for Update message must be tuple, got {ue}.')
1254+
1255+
else:
1256+
logger.error(f'The provided input for Set message (replace operation) is not list.')
1257+
raise gNMIException('The provided input for Set message (replace operation) is not list.')
1258+
1259+
return result

scripts/pygnmicli

+5-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ def main():
5454

5555
elif args.operation == 'get':
5656
print(f'Doing {args.operation} request to {args.target}...')
57-
result = GC.get(path=args.gnmi_path, datatype=args.datastore, encoding='json',
57+
result = GC.get(path=args.gnmi_path,
58+
datatype=args.datastore,
59+
encoding=args.encoding,
5860
target=args.gnmi_path_target)
5961

6062
elif args.operation.startswith('set'):
@@ -70,7 +72,7 @@ def main():
7072
data = f.read()
7173
jdata = json.loads(data)
7274
kwargs[mode] = [(args.gnmi_path[0], jdata)]
73-
result = GC.set(encoding="json_ietf", target=args.gnmi_path_target, **kwargs)
75+
result = GC.set(encoding=args.encoding, target=args.gnmi_path_target, **kwargs)
7476

7577
elif args.operation.startswith('subscribe'):
7678
mode = args.operation.split("-")[1]
@@ -85,7 +87,7 @@ def main():
8587
'subscription': subscription_list,
8688
'use_aliases': False,
8789
'mode': mode,
88-
'encoding': 'json'
90+
'encoding': args.encoding
8991
}
9092

9193
# Set up extensions

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
setup(
77
name='pygnmi',
88
packages=['pygnmi', 'pygnmi.spec.v080', 'pygnmi.artefacts'],
9-
version='0.8.7',
9+
version='0.8.8',
1010
license='bsd-3-clause',
1111
description='Pure Python gNMI client to manage network functions and collect telemetry.',
1212
long_description=long_description,
1313
long_description_content_type='text/x-rst',
1414
author='Anton Karneliuk',
1515
author_email='anton@karneliuk.com',
1616
url='https://github.com/akarneliuk/pygnmi',
17-
download_url='https://github.com/akarneliuk/pygnmi/archive/v0.8.7.tar.gz',
17+
download_url='https://github.com/akarneliuk/pygnmi/archive/v0.8.8.tar.gz',
1818
keywords=['gnmi', 'automation', 'grpc', 'network'],
1919
install_requires=[
2020
'grpcio',

0 commit comments

Comments
 (0)