Develop Off-Device JET Applications
Overview
You can use JET to develop applications that run off-device. This allows you to leverage the benefits of JET on all devices on your network. For ease of development, you can write off-device JET applications in the language of your choice. To develop an off-device application:
Download and compile the IDL file.
Develop the application using the language of your choice.
Package the application.
Deploy the application package on an external server or run the application directly from the JET VM.
Figure 1 shows the off-device application development workflow.
Develop and Package Your Application
Before devloping your application, make sure you have already followed the instructions in Set Up the JET VM to set up the JET VM and download the IDL file.
See Also
Prepare to Deploy Your Application
Run your application on an external server or directly from the JET VM. Before you deploy your application on an external server, you need to configure JET interaction with Junos OS.
Configure JET Interaction with Junos OS
To run an off-device application, you need to enable the
request-response
configuration on Junos OS or Junos OS Evolved. When using the
request-response service, the client application issues a request and
synchronously waits for the response from the Junos server. Use this section to
configure the JET service process (jsd) for the request-response service to run
in Secure Sockets Layer (SSL) mode. This provides increased security and enables
SSL-based API connections.
Currently, JET supports Transport Layer Security (TLS) version 1.2 for certificate exchange and supports multiple encryption algorithms, but does not support mutual authentication. This means that clients can authenticate the server, but the server can not authenticate clients using SSL/TLS certificates. For client authentication, use the LoginCheck() procedure from the authentication service API.
-
Enable jsd to use SSL by adding and configuring the certificate name locally. The certificate must be an RSA certificate. ECDSA and DSA SSL certificates are not supported.
This method is same as other SSL-based services in Junos OS like xnm-ssl. Keep track of the certificate name entry you specify during certificate generation. You will use it for the
HOST_OVERRIDE
option in the example Python application in the next section. In this example, the certificate name isrouter
.user@jet-vm:~ jet$ openssl genrsa -aes256 -out router.key.orig 2048 user@jet-vm:~ jet$ openssl req -new -key router.key.orig -out router.csr user@jet-vm:~ jet$ openssl rsa -in router.key.orig -out router.key user@jet-vm:~ jet$ openssl x509 -req -days 365 -in router.csr -signkey router.key -out router.crt user@jet-vm:~ jet$ cat router.crt router.key > router.pem
Note:If a certificate is updated with the same identifier, the changes will not be reflected for jsd. You need to either configure the certificate with a new identifier in the jsd hierarchy or perform a jsd restart to reflect the changes made.
-
Copy the SSL certificate .pem file to the Junos device.
user@device% scp pem-file-name device-name:/var/tmp
For example:
user@device% scp router.pem device:/var/tmp
-
Load the certificate into the keychain on the Junos device. For example, if the local name of the SSL certificate is
sslcert
:[edit] user@device# set security certificates local sslcert load-key-file /var/tmp/router.pem
-
Enable support for SSL for the loaded certificate.
[edit system services extension-service request-response grpc] user@device# set ssl local-certificate cert-name
For example:
[edit system services extension-service request-response grpc] user@device# set ssl local-certificate sslcert
-
(Optional) Specify the specific IP address or port that will use SSL. SSL makes that address or port a secure channel.
[edit system services extension-service request-response grpc] user@device# set ssl address address user@device# set ssl port port-number
If you set the address to 0.0.0.0, the device uses SSL on all ports. For example, to enable support for SSL on the gRPC endpoint on all ports and the default TCP port 51051:
[edit system services extension-service request-response grpc] user@device# set ssl address 0.0.0.0 user@device# set ssl port 51051
-
Specify the maximum number of simultaneous connections for request-response that can be attached to jsd. The higher the number, the higher the impact on the client’s performance.
[edit system services extension-service request-response grpc] user@device# set max-connections 8
You have configured jsd for request-response service to run in SSL mode. You are ready to deploy your JET off-device application.
-
Specify the scripts to use.
[edit] user@device# set system scripts language python3
Note:Starting in Junos OS Release 21.1R1 and Junos OS Evolved Release 22.3R1, Python 2.7 is no longer supported and the
set system scripts language python
statement is deprecated. Use theset system scripts language python3
statement instead.
Example: Python JET Application
Use this example to develop an off-device JET application written
in Python. You can follow the same guidance for other languages that
are supported by gRPC. This Python JET application runs the command get-system-uptime-information
in XML format.
In this example, the HOST_OVERRIDE
option uses the
certificate name that you specified during the certificate generation.
See Prepare to Deploy Your Application.
Juniper Networks supports both of the following forms
for denoting XML opening and closing tags: <xml-tag/>
and <xml-tag></xml-tag>
.
Junos OS Release 18.4R1 and Later
Use the example Python application shown in this section as a guide if you are using Junos OS Release 18.4R1 or later.
If you are writing your application using Python 3, include the PASS keyword in the Exception block of the script.
except Exception as tx: pass
#!/usr/bin/env python # A simple Python client to run XML OP command 'get-system-uptime-information' # Environment # Python 2.7.12 # grpcio (1.12.0) # grpcio-tools (1.12.0) # Following files should be available in current working directory # jnx_authentication_service_pb2_grpc.py # jnx_authentication_service_pb2.py # jnx_management_service_pb2_grpc.py # jnx_management_service_pb2.py import argparse import grpc import os import stat import jnx_authentication_service_pb2 import jnx_authentication_service_pb2_grpc import jnx_management_service_pb2 import jnx_management_service_pb2_grpc import jnx_common_base_types_pb2 _HOST_OVERRIDE = 'router' def Main(): try: parser = argparse.ArgumentParser() parser.add_argument('-d','--device', help='Input hostname', required=True) parser.add_argument('-t','--timeout', help='Input time_out value', required=True,type=int) parser.add_argument('-u', '--user', help='Input username', required=True) parser.add_argument('-pw', '--password', help='Input password', required=True) args = parser.parse_args() #Establish grpc channel to jet router creds = grpc.ssl_channel_credentials(open('/tmp/router.pem').read(), None, None) channel = grpc.secure_channel(args.device + ":32767", creds, options=(('grpc.ssl_target_name_override', _HOST_OVERRIDE,),)) #create stub for authentication services stub = jnx_authentication_service_pb2_grpc.AuthenticationStub(channel) #Authenticate login_request = jnx_authentication_service_pb2.LoginRequest( username=args.user, password=args.password, client_id="SampleApp") login_response = stub.Login(login_request, args.timeout) #Check if authentication is successful if login_response.status.code == jnx_common_base_types_pb2.SUCCESS: print "[INFO] Connected to gRPC Server" else: print "[ERROR] gRPC Server Connection failed:" print login_response.status.message #Create stub for management services stub = jnx_management_service_pb2_grpc.ManagementStub(channel) print "[INFO] Connected to management service" for i in range(1): #Provide API request details op_xml_command = "<get-system-uptime-information></get-system-uptime-information>" op = jnx_management_service_pb2.OpCommandGetRequest( xml_command=op_xml_command, out_format=2) # Invoke API op_response = stub.OpCommandGet(op, args.timeout) # Check API response like status and output for resp in op_response: if resp.status.code == jnx_common_base_types_pb2.SUCCESS: print "[INFO] Invoked OpCommandGetRequest succeeded" print "[INFO] Return output in CLI format = " print resp.data else: print "[ERROR] Invoked OpCommandGetRequest failed" print "[ERROR] " + resp.status.message except Exception as ex: print ex if __name__ == '__main__': Main()
user@jet-vm:~ jet$ python mgd_api_new_doc_example_ssl.py -d JUNOS_DEVICE -t TIMEOUT -u USER -pw PASSWORD [INFO] Connected to gRPC Server [INFO] Connected to management service [INFO] Invoked OpCommandGetRequest succeeded [INFO] Return output in CLI format = Current time: 2018-11-08 09:36:40 PST Time Source: NTP CLOCK System booted: 2018-10-09 17:02:56 PDT (4w1d 17:33 ago) Protocols started: 2018-10-09 17:05:09 PDT (4w1d 17:31 ago) Last configured: 2018-11-08 09:30:28 PST (00:06:12 ago) by root 9:36AM up 29 days, 17:34, 2 users, load averages: 1.05, 0.77, 0.57
Before Junos OS Release 18.4R1
Use the example Python application in this section as a guide if you are using Junos OS releases prior to 18.4R1.
#!/usr/bin/env python # A simple Python client to run XML OP command 'get-system-uptime-information' # Environment # Python 2.7.12 # grpcio (1.12.0) # grpcio-tools (1.12.0) # Following files should be available in current working directory # authentication_service_pb2_grpc.py # authentication_service_pb2.py # management_service_pb2_grpc.py # management_service_pb2.py import argparse import grpc import authentication_service_pb2 import authentication_service_pb2_grpc import management_service_pb2 import management_service_pb2_grpc _HOST_OVERRIDE = 'router' def Main(): try: parser = argparse.ArgumentParser() parser.add_argument('-d','-device', help='Input hostname', required=True) parser.add_argument('-t','-timeout', help='Input time_out value', required=True,type=int) parser.add_argument('-u', '-user', help='Input username', required=True) parser.add_argument('-pw', '-password', help='Input password', required=True) args = parser.parse_args() #Establish grpc channel to jet router creds = grpc.ssl_channel_credentials(open('/tmp/router.pem').read(), None, None) channel = grpc.secure_channel(args.device + ":51051", creds, options=(('grpc.ssl_target_name_override', _HOST_OVERRIDE,),)) #create stub for authentication services stub = authentication_service_pb2_grpc.LoginStub(channel) #Authenticate login_request = authentication_service_pb2.LoginRequest( user_name=args.user, password=args.password, client_id="SampleApp") login_response = stub.LoginCheck(login_request, args.timeout) #Check if authentication is successful if login_response.result == True: print "[INFO] Connected to gRPC Server:" print login_response.result else: print "[ERROR] gRPC Server Connection failed!!!" print login_response.result #Create stub for management services stub = management_service_pb2_grpc.ManagementRpcApiStub(channel) print "[INFO] Connected to JSD and created handle to mgd services" for i in range(1): #Provide API request details op_xml_command = "<get-system-uptime-information>" \ "</get-system-uptime-information>" op = management_service_pb2.ExecuteOpCommandRequest( xml_command=op_xml_command, out_format=2, request_id=1000) # Invoke API result = stub.ExecuteOpCommand(op, 100) # Check API response like status and output for i in result: print "[INFO] Invoked ExecuteOpCommand API return code = " print i.status print "[INFO] Return output in CLI format = " print i.data except Exception as ex: print ex if __name__ == '__main__': Main()
user@jet-vm:~ jet$ python mgd_api_doc_example_ssl.py -d JUNOS_DEVICE -t TIMEOUT_VAL -u USER -pw PASSWORD [INFO] Connected to gRPC Server: True [INFO] Connected to JSD and created handle to mgd services [INFO] Invoked ExecuteOpCommand API return code = 0 [INFO] Return output in CLI format = Current time: 2018-09-04 11:24:36 PDT Time Source: NTP CLOCK System booted: 2018-08-31 10:58:22 PDT (4d 00:26 ago) Protocols started: 2018-08-31 11:00:52 PDT (4d 00:23 ago) Last configured: 2018-08-31 14:21:32 PDT (3d 21:03 ago) by root 11:24AM up 4 days, 26 mins, 0 users, load averages: 1.20, 1.27, 1.10