gNOI System Service
Use the gNOI System
service to perform system operations on the target
network device, including rebooting the device, upgrading software, and troubleshooting the
network.
Overview
The gNOI System
service provides RPCs to perform a number of different system
operations on a network device, including the following operations:
- Reboot a device
- Execute ping and traceroute commands to troubleshoot the network
- Upgrade software
- Perform a Routing Engine switchover
The proto definition file is located at https://github.com/openconfig/gnoi/blob/master/system/system.proto.
The gnoi-system
process restarts in the event of a system failure. To restart it manually, use the restart gnoi-system
command.
Network Device Configuration
Before you begin:
- Configure gRPC services on the network device as described in Configure gRPC Services.
- Configure the network management system to support gNOI operations as described in Configure gNOI Services.
No additional configuration is required to use the System
service RPCs.
Ping and Traceroute
You can execute ping and traceroute commands on the network device to troubleshoot issues on your network.
RPC | Description | Introduced in Release |
---|---|---|
Ping() |
Ping a device. The Default number of packets: 5 |
Junos OS Evolved 22.2R1 |
Traceroute() |
Execute the traceroute command on the target device and stream back the results. Default hop count: 30 |
Junos OS Evolved 22.2R1 |
Example: Ping
In this example, the client executes the gnoi_ping_request.py
Python application. The application sends the Ping()
RPC to the network device, which then pings the specified device on the network.
The gnoi_ping_request.py
application imports the grpc_channel
module to establish the channel. The grpc_channel
module is described in Configure gNOI Services. The application's arguments are stored in the gnoi_ping_request_args.txt file. The application and argument files are presented here.
gnoi_ping_request.py
"""gRPC gNOI ping request utility.""" from __future__ import print_function import argparse import logging from getpass import getpass import system_pb2 import system_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost') parser.add_argument('--port', dest='port', nargs='?', type=int, default=32767, help='The server port. Default is 32767') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default ""') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default ""') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') # Ping request arguments parser.add_argument('--destination', dest='destination', type=str, default=None, help='Destination IP. Default None') parser.add_argument('--source', dest='source', type=str, default=None, help='Source IP. Default None') parser.add_argument('--count', dest='count', type=int, default=None, help='Count of packets. Default None') parser.add_argument('--interval', dest='interval', type=int, default=None, help='Interval of packets in nanoseconds. Default None') parser.add_argument('--wait', dest='wait', type=int, default=None, help='Wait of packets in nanoseconds. Default None') parser.add_argument('--size', dest='size', type=int, default=None, help='Size of packets. Default None') parser.add_argument('--dnfragment', dest='dnfragment', type=int, default=0, help='Do not fragment. Default 0 (False)') parser.add_argument('--dnresolve', dest='dnresolve', type=int, default=0, help='Do not resolve. Default 0 (False)') parser.add_argument('--l3protocol', dest='l3protocol', type=int, default=None, help='L3 protocol (1=ipv4,2=ipv6). Default None') parser.add_argument('--timeout', dest='timeout', type=int, default=30, help='Timeout for ping. Default: 30 seconds') args = parser.parse_args() return args def send_rpc(channel, metadata, args): stub = system_pb2_grpc.SystemStub(channel) print("Executing GNOI::System::Ping Request RPC") req = system_pb2.PingRequest() if args.count != None: req.count = args.count if args.source != None: req.source = args.source if args.destination != None: req.destination = args.destination if args.interval != None: req.interval = args.interval if args.wait != None: req.wait = args.wait if args.size != None: req.size = args.size if args.dnfragment != 0: req.do_not_fragment = args.dnfragment if args.dnresolve != 0: req.do_not_resolve = args.dnresolve if args.l3protocol != None: req.l3protocol = args.l3protocol try: print("Ping Request Response starts\n") count = 1 for ping in stub.Ping(request=req, metadata=metadata, timeout=args.timeout): print("Response Source%s: %s " % (count, ping.source)) print("Time%s: %s" % (count, ping.time)) print("Sent%s: %s" % (count, ping.sent)) print("Receive%s: %s" % (count, ping.received)) print("Mintime%s: %s" % (count, ping.min_time)) print("Avgtime%s: %s" % (count, ping.avg_time)) print("Stddev%s: %s" % (count, ping.std_dev)) print("Bytes%s: %s" % (count, ping.bytes)) print("Sequence%s: %s" % (count, ping.sequence)) print("TTL%s: %s" % (count, ping.ttl)) count += 1 print("Ping Request Response ends\n") except Exception as e: logging.error('Error: %s', e) print(e) def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) response = send_rpc(channel, metadata, args) except Exception as e: logging.error('Received error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
gnoi_ping_request_args.txt
--server=10.0.2.1 --port=32767 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --client_key=/home/lab/certs/client.key --client_cert=/home/lab/certs/client.crt --user_id=gnoi-user --destination=10.0.3.1 --source=10.0.2.1 --count=5
Execute the Application
On the client, execute the application, which prompts for the server's password for the RPC call credentials. The PingResponse
indicates that the device sent five pings. The final response includes the summary statistics for the ping request, which shows that the device sent five pings and received five responses.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_ping_request.py @gnoi_ping_request_args.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::System::Ping Request RPC Ping Request Response starts Response Source1: 10.0.3.1 Time1: 741000 Sent1: 0 Receive1: 0 Mintime1: 0 Avgtime1: 0 Stddev1: 0 Bytes1: 64 Sequence1: 1 TTL1: 59 Response Source2: 10.0.3.1 Time2: 734000 Sent2: 0 Receive2: 0 Mintime2: 0 Avgtime2: 0 Stddev2: 0 Bytes2: 64 Sequence2: 2 TTL2: 59 Response Source3: 10.0.3.1 Time3: 704000 Sent3: 0 Receive3: 0 Mintime3: 0 Avgtime3: 0 Stddev3: 0 Bytes3: 64 Sequence3: 3 TTL3: 59 Response Source4: 10.0.3.1 Time4: 767000 Sent4: 0 Receive4: 0 Mintime4: 0 Avgtime4: 0 Stddev4: 0 Bytes4: 64 Sequence4: 4 TTL4: 59 Response Source5: 10.0.3.1 Time5: 800000 Sent5: 0 Receive5: 0 Mintime5: 0 Avgtime5: 0 Stddev5: 0 Bytes5: 64 Sequence5: 5 TTL5: 59 Response Source6: 10.0.3.1 Time6: 4111000000 Sent6: 5 Receive6: 5 Mintime6: 704000 Avgtime6: 749000 Stddev6: 32000 Bytes6: 0 Sequence6: 0 TTL6: 0 Ping Request Response ends
Reboot a Device
Use the System
service RPCs to remotely reboot a device, check the status of the
reboot, and cancel the reboot if needed. You can execute these RPCs on the device or
on specific subcomponents. Junos devices support the following reboot methods:
COLD (1): Available for all types of reboots.
POWERDOWN (2): Use for FPC reboots.
HALT (3): Use for active Control Processor reboots.
POWERUP (7): Use for FPC reboots.
RPC | Description | Introduced in Release |
---|---|---|
Reboot() |
Reboot the target. You can only execute one reboot request on a target at a time. You can optionally configure a delay to reboot in the future, reboot subcomponents individually, and add a message when the reboot initiates. The delay is configured in nanoseconds. Junos devices support the following reboot methods:
|
Junos OS Evolved 22.2R1 |
RebootStatus() |
Return the status of the reboot. | Junos OS Evolved 22.2R1 |
CancelReboot() |
Cancel a pending reboot request. | Junos OS Evolved 22.2R1 |
Example: Reboot
In this example, the client executes the gnoi_reboot_request.py
Python application. The application sends the reboot request and then checks the status of the reboot.
The application lets the user set the reboot delay in seconds. Since the RebootRequest()
interprets the delay in nanoseconds, the application converts the user input into nanoseconds for the request. In this example, the client specifies a 60-second delay for the reboot operation.
The gnoi_reboot_request.py
application imports the grpc_channel
module to establish the channel. The grpc_channel
module is described in Configure gNOI Services. The application's arguments are stored in the reboot_status_request_args.txt file. The application and argument files are presented here.
gnoi_reboot_status_request.py
"""gRPC gNOI reboot request and reboot status utility.""" from __future__ import print_function import argparse import logging from getpass import getpass import types_pb2 import system_pb2 import system_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost') parser.add_argument('--port', dest='port', nargs='?', type=int, default=32767, help='The server port. Default is 32767') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default ""') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default ""') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') # Arguments for RebootRequest parser.add_argument('--method', dest='method', type=int, default=1, help='Reboot method. Valid value: 0 (UNKNOWN), 1 (COLD), 2 (POWERDOWN), 3 (HALT), 6 (reserved), 7 (POWERUP). Default 1') parser.add_argument('--delay', dest='delay', type=int, default=None, help='Delay in seconds before rebooting. Default 0') parser.add_argument('--message', dest='message', type=str, default=None, help='Message for rebooting.') parser.add_argument('--force', dest='force', type=int, default=None, help='Force reboot. Valid value 0|1. Default 0') parser.add_argument('--subcomponents', dest='subcomponents', type=str, default='', help='Subcomponents to reboot. Valid value re0,re1,fpc0,fpc8,etc. Default ""') args = parser.parse_args() return args def send_rpc(channel, metadata, args): # RebootRequest stub = system_pb2_grpc.SystemStub(channel) print("Executing GNOI::System::Reboot RPC") req = system_pb2.RebootRequest() # Add RebootRequest arguments req.method = args.method if args.delay != None: # gNOI delay is in nanoseconds. Convert from seconds to nanoseconds. req.delay = args.delay*(10**9) if args.message != None: req.message = args.message if args.force != None: req.force = args.force for subcomponent in args.subcomponents.split(","): if subcomponent == "": continue elem_key = {} elem_key["%s" % subcomponent] = subcomponent path_elem = [types_pb2.PathElem( name="%s" % subcomponent, key=elem_key)] path = types_pb2.Path(origin="origin", elem=path_elem) req.subcomponents.extend([path]) # RebootStatus print("Executing GNOI::System::Reboot Status RPC") req_status = system_pb2.RebootStatusRequest() try: reboot_response = stub.Reboot( request=req, metadata=metadata, timeout=60) status_response = stub.RebootStatus( request=req_status, metadata=metadata, timeout=60) print("Reboot status response received. %s" % status_response) except Exception as e: logging.error('Error: %s', e) print(e) else: logging.info('Received reboot status: %s', status_response) def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) send_rpc(channel, metadata, args) except Exception as e: print(e) logging.error('Received error: %s', e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
reboot_status_request_args.txt
--server=10.0.2.1 --port=32767 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --client_key=/home/lab/certs/client.key --client_cert=/home/lab/certs/client.crt --user_id=gnoi-user --message="Testing gNOI reboot" --delay=60
Execute the Application
When the client executes the application, the application prompts for the server's password for the RPC call credentials. The application then reboots the server after a 60 second delay and returns the applicable reboot status messages. The message set under reason
also appears on the server immediately before the server reboots. In this example, any user logged into the server sees "Testing gNOI reboot" immediately before it reboots.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_reboot_status_request.py @reboot_status_request_args.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::System::Reboot RPC Executing GNOI::System::Reboot Status RPC Reboot status response received! active: true wait: 59266969677 when: 1651788480000000000 reason: "Testing gNOI reboot" count: 5
Upgrade Software
Table 3 lists the System service RPCs that support software upgrades.
RPC | Description | Introduced in Release |
---|---|---|
SetPackage() |
Install a software image on the target device. |
Junos OS Evolved 22.2R1 |
You can use the SetPackage()
RPC to copy a software image to the target device and install it. The source software image must reside on the local network management system. If the file copy operation is successful, and a file of the same name already exists at the target location, the file is overwritten. The RPC returns an error if the target location does not exist or if there is an error writing the data.
By default, SetPackage()
does not reboot the device and activate the software. You must explicitly set the activate
option to 1 in the SetPackageRequest
message to activate the new software. If you activate the software, the device reboots and uses the new software image. If you do not activate the software, you must reboot the relevant nodes to complete the installation and activate the new software image.
Example: Install a Software Package
In this example, the client executes the gnoi_system_set_package.py
Python application, which performs the following operations:
- Copies the software package from the local network management system to the network device.
- Installs the package on the network device.
- Reboots the network device, thus activating the new software image.
The application constructs the SetPackageRequest
message with the appropriate parameters to define the request for the copy and install operations. The application then calls the SetPackage()
RPC to send the request to the network device. The SetPackageRequest
message contains the following components:
- An initial
Package
message containing the path and file information for the software image. Theactivate
argument is set to 1 (True
) to reboot the device and activate the software. - A stream of the software image file contents in sequential messages that do not exceed 64KB.
- A final message with the file checksum to verify the integrity of the file contents.
The gnoi_system_set_package.py
application imports the grpc_channel
module to establish the channel. The grpc_channel
module is described in Configure gNOI Services. The application's arguments are stored in the args_system_set_package.txt
file. The application and argument files are as follows:
gnoi_system_set_package.py
"""gRPC gNOI OS Upgrade Utility.""" from __future__ import print_function import argparse import hashlib import logging from functools import partial from getpass import getpass import system_pb2 import system_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual MAX_BYTES = 65536 def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost') parser.add_argument('--port', dest='port', nargs='?', type=int, default=32767, help='The server port. Default is 32767') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default ""') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default ""') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') parser.add_argument('--activate', dest='activate', type=int, default=0, help='Reboot and activate the package. Default: 0 (Do not reboot/activate). Valid value: 1 (Reboot/activate).') parser.add_argument('--filename', dest='filename', type=str, default='', help='Destination path and filename of the package. Default ""') parser.add_argument('--source_package', dest='source_package', type=str, default='', help='Full path of the source file to send. Default ""') parser.add_argument('--timeout', dest='timeout', type=int, default=None, help='Timeout in seconds.') parser.add_argument('--version', dest='version', type=str, default='', help='Version of the package. Default ""') args = parser.parse_args() return args def send_rpc(channel, metadata, args): stub = system_pb2_grpc.SystemStub(channel) print("Executing GNOI::System::SetPackage") # Create request # Add file information to request req = system_pb2.SetPackageRequest() req.package.activate = args.activate req.package.filename = args.filename it = [] it.append(req) # Prepare hash generator gen_hash = hashlib.sha256() # Read source package and add to request with open(args.source_package, "rb") as fd: # Read data in 64 KB chunks and calculate checksum and data messages for data in iter(partial(fd.read, MAX_BYTES), b''): req = system_pb2.SetPackageRequest() req.contents = data it.append(req) gen_hash.update(data) # Add checksum to request req = system_pb2.SetPackageRequest() req.hash.hash = gen_hash.hexdigest().encode() req.hash.method = 1 it.append(req) # Install the package try: logging.info('Installing package %s', args.source_package) print('SetPackage start.') response = stub.SetPackage( iter(it), metadata=metadata, timeout=args.timeout) print('SetPackage complete.') except Exception as e: logging.error('Software install error: %s', e) print(e) else: logging.info('SetPackage complete.') return response def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) response = send_rpc(channel, metadata, args) except Exception as e: logging.error('Error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-install.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
args_system_set_package.txt
--server=10.0.2.1 --port=32767 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --client_key=/home/lab/certs/client.key --client_cert=/home/lab/certs/client.crt --user_id=gnoi-user --activate=1 --filename=/var/tmp/junos-evo-install-ptx-x86-64-22.2R1.13-EVO.iso --source_package=/home/lab/images/junos-evo-install-ptx-x86-64-22.2R1.13-EVO.iso --timeout=1800
Execute the Application
When the client executes the application, the application copies the package from the local device to the network device, installs it, and then reboots the device to complete the installation.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_system_set_package.py @args_system_set_package.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::System::SetPackage SetPackage start. SetPackage complete.
Routing Engine Switchover
You can use the SwitchControlProcessor()
RPC to perform a Routing Engine switchover.
Successive Routing Engine switchover events must be a minimum of 400 seconds apart after both Routing Engines have come up.
RPC | Description | Introduced in Release |
---|---|---|
SwitchControlProcessor() |
Switch from the current Routing Engine to the specified Routing Engine. If the current and specified Routing Engines are the same, it is a NOOP. If the target does not exist, the RPC returns an error. Note:
Junos devices do not support |
Junos OS Evolved 22.2R1 |
Example: Routing Engine Switchover
In this example, the gNOI client executes the gnoi_system_switch_control_processor.py
application to perform a Routing Engine switchover. The client specifies which switch control processor, or Routing Engine, should be the primary Routing Engine by including the control_processor
argument. If the target Routing Engine does not exist, the RPC returns an INVALID_ARGUMENT
error.
The application imports the grpc_channel
module to establish the channel. The grpc_channel
module is described in Configure gNOI Services.
gnoi_system_switch_control_processor.py
"""gNOI Routing Engine switchover request utility.""" from __future__ import print_function import argparse import logging from getpass import getpass import system_pb2 import system_pb2_grpc import types_pb2 from grpc_channel import grpc_authenticate_channel_mutual def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost') parser.add_argument('--port', dest='port', nargs='?', type=int, default=32767, help='The server port. Default is 32767') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default ""') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default ""') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') parser.add_argument('--control_processor', dest='control_processor', type=str, default='re1', help='Control processor that will assume the role of primary. Default is re1. Valid values are re0,re1.') args = parser.parse_args() return args def send_rpc(channel, metadata, processor): stub = system_pb2_grpc.SystemStub(channel) print("Executing GNOI::System::SwitchControlProcessor") elem_key = {} elem_key["%s" % processor] = processor path_elem = [types_pb2.PathElem(name="%s" % processor, key=elem_key)] path = types_pb2.Path(origin="origin", elem=path_elem) req = system_pb2.SwitchControlProcessorRequest(control_processor=path) # Send the request try: response = stub.SwitchControlProcessor( req, metadata=metadata, timeout=60) print("SwitchControlProcessor response:\n", response) except Exception as e: logging.error('Switchover error: %s', e) print(e) else: logging.info('SwitchControlProcessor response:\n %s', response) return response def main(): parser = argparse.ArgumentParser() args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) response = send_rpc(channel, metadata, args.control_processor) except Exception as e: logging.error('Received error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
Execute the Application
The client executes the application and sets the control_processor
argument to re1
so that re1 becomes the primary Routing Engine.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_system_switch_control_processor.py --server 10.0.2.1 --port 32767 --root_ca_cert /etc/pki/certs/serverRootCA.crt --client_key /home/lab/certs/client.key --client_cert /home/lab/certs/client.crt --user_id gnoi-user --control_processor re1 gRPC server password for executing RPCs: Creating channel Executing GNOI::System::SwitchControlProcessor SwitchControlProcessor response: version: "22.2R1.13-EVO" uptime: 1652478709000000000
After executing the operation, re1 is the primary Routing Engine on the target device.
{master} lab@gnoi-server-re1>