gNOI System Service
SUMMARY 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=50051, help='The server port. Default is 50051') 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=50051 --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=50051, help='The server port. Default is 50051') 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=50051 --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.proto RPC that supports 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=50051, help='The server port. Default is 50051') 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=50051 --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=50051, help='The server port. Default is 50051') 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 50051 --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>