Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update py_gnmicli.py for gNMI Subscribe support #65

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions gnmi_cli_py/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ gNMI SetRequest Replace for an Access Point target, who's hostname is "ap-1", wi
```
python py_gnmicli.py -t example.net -p 443 -m set-replace -x /access-points/access-point[hostname=test-ap1]/radios/radio[id=0]/config/channel -user admin -pass admin -rcert ca.cert.pem -val 165
```
gNMI Subscribe request with Subscribe Mode STREAM and Submode SAMPLE with sample interval 5 seconds.
```
python py_gnmicli.py -t example.net -p 443 -m subscribe -x /access-points/access-point[hostname=test-ap1]/radios/radio[id=0]/config/channel -user admin -pass admin -g -o openconfig.com --interval 5 --subscribe_mode 0 --submode 2
```
The above SetRequest Replace would output the following to stdout:
```
Performing SetRequest Replace, encoding=JSON_IETF to openconfig.example.com with the following gNMI Path
Expand Down
107 changes: 100 additions & 7 deletions gnmi_cli_py/py_gnmicli.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@
print('ERROR: Ensure you\'ve installed dependencies from requirements.txt\n'
'eg, pip install -r requirements.txt')
import gnmi_pb2_grpc
import grpc

__version__ = '0.3'

_RE_PATH_COMPONENT = re.compile(r'''
^
(?P<pname>[^[]+) # gNMI path name
(\[(?P<key>\w+) # gNMI path key
(\[(?P<key>[a-zA-Z0-9\-]+) # gNMI path key
=
(?P<value>.*) # gNMI path value
\])?$
Expand Down Expand Up @@ -140,6 +141,20 @@ def _create_parser():
required=False, action='store_true')
parser.add_argument('-n', '--notls', help='gRPC insecure mode',
required=False, action='store_true')
parser.add_argument('--interval', default=10, type=int,
help='sample interval (default: 10s)')
parser.add_argument('--timeout', type=int, help='subscription'
'duration in seconds (default: none)')
parser.add_argument('--heartbeat', default=0, type=int, help='heartbeat interval (default: None)')
parser.add_argument('--aggregate', action='store_true', help='allow aggregation')
parser.add_argument('--suppress', action='store_true', help='suppress redundant')
parser.add_argument('--submode', default=2, type=int,
help='subscription mode [0=TARGET_DEFINED, 1=ON_CHANGE, 2=SAMPLE]')
parser.add_argument('--subscribe_mode', default=0, type=int, help='[0=STREAM, 1=ONCE, 2=POLL]')
parser.add_argument('--encoding', default=0, type=int, help='[0=JSON, 1=BYTES, 2=PROTO, 3=ASCII, 4=JSON_IETF]')
parser.add_argument('--qos', default=0, type=int, help='')
parser.add_argument('--use_alias', action='store_true', help='use alias')
parser.add_argument('--prefix', default='', help='gRPC path prefix (default: none)')
return parser


Expand All @@ -154,9 +169,31 @@ def _path_names(xpath):
Returns:
list of gNMI path names.
"""
if not xpath or xpath == '/': # A blank xpath was provided at CLI.
return []
return xpath.strip().strip('/').split('/') # Remove leading and trailing '/'.
path = []
insidebracket = False
begin = 0
end = 0
xpath=xpath+'/'
while end < len(xpath):
if xpath[end] == "/":
if insidebracket == False:
if end > begin:
path.append(xpath[begin:end])
end = end + 1
begin = end
else:
end = end + 1
elif xpath[end] == "[":
if (end==0 or xpath[end-1]!='\\') and insidebracket == False:
insidebracket = True
end = end + 1
elif xpath[end] == "]":
if (end==0 or xpath[end-1]!='\\') and insidebracket == True:
insidebracket = False
end = end + 1
else:
end = end + 1
return path


def _parse_path(p_names):
Expand Down Expand Up @@ -248,7 +285,7 @@ def _get_val(json_value):
json_value.strip('@'), 'rb').read())
except (IOError, ValueError) as e:
raise JsonReadError('Error while loading JSON: %s' % str(e))
val.json_ietf_val = json.dumps(set_json)
val.json_ietf_val = json.dumps(set_json).encode('utf-8')
return val
coerced_val = _format_type(json_value)
type_to_value = {bool: 'bool_val', int: 'int_val', float: 'float_val',
Expand Down Expand Up @@ -349,6 +386,62 @@ def _open_certs(**kwargs):
return kwargs


def gen_request(paths, opt):
"""Create subscribe request for passed xpath.
Args:
paths: (str) gNMI path.
opt: (dict) Command line argument passed for subscribe reqeust.
Returns:
gNMI SubscribeRequest object.
"""
mysubs = []
mysub = gnmi_pb2.Subscription(path=paths, mode=opt["submode"], sample_interval=opt["interval"]*1000000000, heartbeat_interval=opt['heartbeat']*1000000000, suppress_redundant=opt['suppress'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix line length.

mysubs.append(mysub)
if opt["prefix"]:
myprefix = _parse_path(_path_names(opt["prefix"]))
else:
myprefix = None

if opt["qos"]:
myqos = gnmi_pb2.QOSMarking(marking=opt["qos"])
else:
myqos = None
mysblist = gnmi_pb2.SubscriptionList(prefix=myprefix, mode=opt['subscribe_mode'], allow_aggregation=opt['aggregate'], encoding=opt['encoding'], subscription=mysubs, use_aliases=opt['use_alias'], qos=myqos)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line length

mysubreq = gnmi_pb2.SubscribeRequest(subscribe=mysblist)

print('Sending SubscribeRequest\n'+str(mysubreq))
yield mysubreq


def subscribe_start(stub, options, req_iterator):
""" RPC Start for Subscribe reqeust
Args:
stub: (class) gNMI Stub used to build the secure channel.
options: (dict) Command line argument passed for subscribe reqeust.
req_iterator: gNMI Subscribe Request from gen_request.
Returns:
Start Subscribe and printing response of gNMI Subscribe Response.
"""
metadata = [('username', options['username']), ('password', options['password'])]
try:
responses = stub.Subscribe(req_iterator, options['timeout'], metadata=metadata)
for response in responses:
if response.HasField('sync_response'):
print('Sync Response received\n'+str(response))
elif response.HasField('error'):
print('gNMI Error '+str(response.error.code)+' received\n'+str(response.error.message) + str(response.error))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line length

elif response.HasField('update'):
print(response)
else:
print('Unknown response received:\n'+str(response))
except KeyboardInterrupt:
print("Subscribe Session stopped by user.")
except grpc.RpcError as x:
print("grpc.RpcError received:\n%s" %x)
except Exception as err:
print(err)


def main():
argparser = _create_parser()
args = vars(argparser.parse_args())
Expand Down Expand Up @@ -407,8 +500,8 @@ def main():
response = _set(stub, paths, 'delete', user, password, json_value)
print('The SetRequest response is below\n' + '-'*25 + '\n', response)
elif mode == 'subscribe':
print('This mode not available in this version')
sys.exit()
request_iterator = gen_request(paths, args)
subscribe_start(stub, args, request_iterator)


if __name__ == '__main__':
Expand Down