gNOI診断(診断)サービス
概要 gNOI 診断(Diag
)サービスを使用して、2 台のデバイス間のリンクの信頼性をテストします。
概要
サービス RPC を Diag
使用して、1 組の接続ポートでビット 誤り率テスト(BERT)を実行します。 Diag
サービス proto 定義ファイルは 、https://github.com/openconfig/gnoi/blob/master/diag/diag.proto にあります。
BERT は、擬似ランダム バイナリ シーケンス(PRBS)テストとも呼ばれ、リンクの信頼性をテストします。gNOI RPC は StartBERT()
、接続された物理インターフェイスのペアで双方向 BERT を開始します。デバイスは、リンクを介して1と0のセットパターンを交換します。デバイスは、受信したメッセージと送信されたメッセージを比較し、エラー数をカウントします。エラーの数が少ないほど、リンクの品質が高くなります。
デバイスが結果を比較できるように、リンクの両側で gNOI BERT を実行する必要があります。テストするリンクは BERT の間にダウンし、BERT が終了すると元に戻ります。ただし、BERT を実行しているデバイスの 1 つが再起動した場合、もう一方のデバイスで BERT を停止しない限り、リンクはダウンしたままになります。
複数の所定のタイプからテストパターンを選択できます。BERT または PRBS テスト パターンのタイトルは PRBSx 形式で、x は数字です。Junos デバイスは、gNOI BERTs で以下のテスト パターンをサポートしています。
- PRBS7
- PRBS9
- PRBS15
- PRBS23
- PRBS31
各 gNOI BERT に固有の操作 ID を付与する必要があります。BERT を開始し、BERT を停止し、BERT の結果を取得する RPC は、BERT 操作 ID によってリンクされます。新しい BERT を実行する場合、操作 ID を新しい文字列に変更する必要があります。RPC は各 BERT を動作 ID で識別するため、同じ ID を持つ異なるインターフェイス上で複数の BERT を実行できます。
デバイスは、最後の 5 つの BERT 操作の結果を保持します。ただし、保存された BERT 結果は永続的ではありません。システムが再起動すると、それらは失われます。
保存された特定の BERT 操作の結果を表示するには、目的の BERT 操作 ID にメッセージを送信 GetBERTResultRequest
し、 フィールドを result_from_all_ports
に False
設定します。異なる ID のすべての要求結果を表示するには、メッセージ内のフィールドを result_from_all_ports
GetBERTResultRequest
に設定します True
。
デバイスで RPC を GetBERTResult()
実行すると、RPC は BERT で検出された特定のデバイスのビット数を表示します。RPC には合格または不合格条件が設定されていないため、結果を評価するのはユーザー次第です。次のようないくつかの理由で、多数のエラーが発生する可能性があります。
- リンクの品質が低い。
- BERT の実行中に、デバイスの 1 つがオフラインになりました。
- BERT は 1 つのデバイスのみで実行されます。
- BERT は両方のデバイスで同時に開始および停止しませんでした。
最後のエラーを回避するには、RPC を両方の StartBERT()
デバイスに同時に送信することをお勧めします。あるデバイス上でもう一方のデバイスで BERT を起動した場合、BERT がもう一方のデバイスで開始されるまで、最初のデバイスは応答を受信しません。最初のデバイスは、応答の欠如をビットの不一致として記録します。1 台目のデバイスは、2 台目のデバイスで BERT が起動するまでエラーの報告を続けます。BERT を同時に起動できない場合は、最後に BERT を起動した GetBERTResult()
デバイスで RPC を実行することをお勧めします。BERT がすでに最初のデバイスで実行されていたため、2 番目のデバイスはビットの欠落を報告しません。
サポートされている RPC
リリースで導入された | RPC | の説明 |
---|---|---|
StartBERT() |
一連のポートで BERT を起動します。Junos デバイスは、gNOI BERTs に対して以下の PRBS パターンをサポートしています。
|
Junos OS Evolved 22.2R1 |
StopBert() |
一連のポートで、すでに進行中の BERT を停止します。 |
Junos OS Evolved 22.2R1 |
GetBERTResult() |
BERT の実行中または完了後に BERT の結果を取得します。 |
Junos OS Evolved 22.2R1 |
ネットワーク デバイスの設定
- gRPCサービスの設定に関する説明に従って、ネットワークデバイス上で gRPCサービスを設定します。
- gNOI サービスの構成に関する説明に従って、gNOI 操作をサポートするようにネットワーク管理システムを構成します。
- BERT を実行するリンクについては、サーバーとピア インターフェイスの速度が一致するように設定します。BERT は、インターフェイス速度が一致する場合にのみ実行されます。
例: BERT を実行する
インターフェイス上で BERT が進行中の間、そのインターフェイス上の物理リンクはダウンします。
gNOI クライアントとサーバーを構成した後、アプリケーションを作成して実行して BERT を実行する準備が整いました。この例では、クライアントは Python アプリケーションを gnoi_bert_client.py
実行して、サーバーとピア デバイス間のリンクをテストします。アプリケーションは gnoi_bert_client.py
、引数に応じて BERT の開始、BERT の停止、または BERT 結果の取得ができます。
まず、クライアントは RPC の送信にStartBERT()
使用gnoi_bert_client.py
して、サーバーとピアで BERT を起動します。BERT の実行中、サーバーとピアは et-1/0/2 と et-1/0/3 インターフェイス間のリンクを介して BERT テスト パケットを交換します。
BERT は、設定した時間の期限が切れた後に終了します。次に、クライアントは RPC で GetBERTResult()
アプリケーションを 2 回実行し、サーバーから BERT の結果を取得します。
メッセージの StartBERTRequest
パラメーターは、 input_bert_start.json JSON ファイルに格納されます。このファイルは、PRBS パターン 31 を使用して BERT を 60 秒間実行することを指定します。メッセージの GetBERTResultRequest
パラメーターは 、input_bert_get.json JSON ファイルに格納されます。フィールドは result_from_all_ports
、 に False
設定されているため、 GetBERTResult()
RPC はこのポートからこの特定の BERT の結果のみを取得します。BERT 操作 ID は BERT-operation id 1
両方の JSON ファイルに含まれます。
アプリケーションは、チャネルを grpc_channel
確立するためにモジュールをインポートします。このモジュールについては grpc_channel
、「 gNOI サービスの設定」を参照してください。ここには、アプリケーション・ファイルと JSON ファイルが表示されています。
gnoi_bert_client.py
"""gRPC gNOI BERT utility.""" from __future__ import print_function import argparse import json import sys import logging from getpass import getpass import diag_pb2 import diag_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual from google.protobuf.json_format import MessageToJson from google.protobuf.json_format import ParseDict def get_args(parser): # Main arguments 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.') # BERT arguments parser.add_argument('--input_file', dest='input_file', type=str, default=None, help='Input JSON file to convert to a Message Object. Default NULL string') parser.add_argument('--output_file', dest='output_file', type=str, default=None, help='Output file. Default NULL string') parser.add_argument('--message', dest='message', type=str, default=None, help='The type of Message Object. Must correspond to input file JSON. Default NULL string') args = parser.parse_args() return args def check_inputs(args): # Check each of the default=None arguments if args.server is None: print('\nFAIL: --server is not passed in\n') return False if args.port is None: print('\nFAIL: server port (--port) is not passed in\n') return False if args.input_file is None: print('\nFAIL: --input_file is not passed in\n') return False if args.output_file is None: print('\nFAIL: --output_file is not passed in\n') return False if args.message is None: print('\nFAIL: --message is not passed in\n') return False return True # Create a dictionary where top-level keys match what is passed in via args.message # The values are pointers to the relevant classes and method names needed to build/send message objects MESSAGE_RELATED_OBJECTS = { 'StartBERTRequest': { 'msg_type': diag_pb2.StartBERTRequest, 'grpc': diag_pb2_grpc.DiagStub, 'method': 'StartBERT' }, 'StopBERTRequest': { 'msg_type': diag_pb2.StopBERTRequest, 'grpc': diag_pb2_grpc.DiagStub, 'method': 'StopBERT' }, 'GetBERTResultRequest': { 'msg_type': diag_pb2.GetBERTResultRequest, 'grpc': diag_pb2_grpc.DiagStub, 'method': 'GetBERTResult' } } def send_rpc(channel, metadata, args): if not check_inputs(args): print('\nFAIL: One of the inputs was not as expected.\n') return False print('\nMessage Type is {}'.format(args.message)) # Message objects to send msg_object_list = [] with open(args.input_file) as json_file: user_input = json.load(json_file) # Choose the Request Message Object type based on the --message type passed request_message = MESSAGE_RELATED_OBJECTS[args.message]['msg_type']() # Convert the dictionary to the type of message object specified by request_message try: msg_object_list.append(ParseDict(user_input, request_message)) except Exception as error: print('\n\nError:\n{}'.format(error), file=sys.stderr) raise # Assemble callable object to use for sending, e.g. diag_pb2_grpc.DiagStub(channel).StartBERT method = MESSAGE_RELATED_OBJECTS[args.message]['method'] send_message = getattr( MESSAGE_RELATED_OBJECTS[args.message]['grpc'](channel), method) # send the Request Object(s) for msg_object in msg_object_list: resp = send_message(msg_object, metadata=metadata) print('\n\nResponse:\n{}'.format(resp)) print('=================================') resp_json = MessageToJson(resp) print('\n\nResponse JSON:\n{}'.format(resp_json)) with open(args.output_file, 'w') as data: data.write(str(resp_json)) return True 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) 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()
input_bert_start.json
{ "bert_operation_id": "BERT-operation id 1", "per_port_requests": [ { "interface": { "origin": "origin", "elem": [ {"name": "interfaces"}, {"name": "interface", "key": {"name": "et-1/0/2"}} ] }, "prbs_polynomial": "PRBS_POLYNOMIAL_PRBS31", "test_duration_in_secs": "60" } ] }
input_bert_get.json
{ "bert_operation_id": "BERT-operation id 1", "result_from_all_ports": false, "per_port_requests": [ { "interface": { "origin": "origin", "elem": [ {"name": "interfaces"}, {"name": "interface", "key": {"name": "et-1/0/2"}} ] } } ] }
アプリケーションの実行
-
クライアントから、アプリケーションを
gnoi_bert_client.py
実行して、ピアで BERT を開始します(図示しません)。次に、アプリケーションをgnoi_bert_client.py
実行してサーバー上で BERT を起動します(以下を参照)。BERT を開始するには、 にStartBERTRequest
設定message
し、input_bert_start.json ファイル パスに設定input_file
します。各デバイスについて、入力ファイルには、そのデバイスでテストされたインターフェイスを指定する必要があります。ステータスはBERT_STATUS_OK
、BERT が正常に開始したことを示しています。lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_bert_client.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 --message StartBERTRequest --input_file diag/input_bert_start.json --output_file diag/output/bert-start-resp1.json gRPC server password for executing RPCs: Message Type is StartBERTRequest Response: bert_operation_id: "BERT-operation id 1" per_port_responses { interface { origin: "origin" elem { name: "interfaces" } elem { name: "interface" key { key: "name" value: "et-1/0/2" } } } status: BERT_STATUS_OK } ================================= Response JSON: { "bertOperationId": "BERT-operation id 1", "perPortResponses": [ { "interface": { "origin": "origin", "elem": [ { "name": "interfaces" }, { "name": "interface", "key": { "name": "et-1/0/2" } } ] }, "status": "BERT_STATUS_OK" } ] }
-
(オプション)BERT を実行している間、サーバーまたはピア デバイスで コマンドを使用
show interfaces
して、継続的な BERT 結果を表示します。BERT が実行されている場合、PRBS モードはEnabled
.この例の出力は、明確にするために切り捨てられます。user@server> show interfaces et-1/0/2 Physical interface: et-1/0/2, Enabled, Physical link is Down Interface index: 1018, SNMP ifIndex: 534 [...] PRBS Mode : Enabled PRBS Pattern : 31 PRBS Statistics Lane 0 : Error Bits : 0 Total Bits : 200000000000 Monitored Seconds : 8 Lane 1 : Error Bits : 0 Total Bits : 200000000000 Monitored Seconds : 8 Lane 2 : Error Bits : 0 Total Bits : 200000000000 Monitored Seconds : 8 Lane 3 : Error Bits : 0 Total Bits : 200000000000 Monitored Seconds : 8 Interface transmit statistics: Disabled Link Degrade : Link Monitoring : Disable [...]
-
BERT が終了したら、 に
GetBERTResultRequest
設定してアプリケーションをgnoi_bert_client.py
再度message
実行しinput_file
、input_bert_get.json ファイル パスに設定してテストの結果を取得します。この例では、BERT は 1 分間のテスト中にエラーを検出しました。lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_bert_client.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 --message GetBERTResultRequest --input_file diag/input_bert_get.json --output_file diag/output/bert-get-resp1.json gRPC server password for executing RPCs: Message Type is GetBERTResultRequest Response: per_port_responses { interface { origin: "origin" elem { name: "interfaces" } elem { name: "interface" key { key: "name" value: "et-1/0/2" } } } status: BERT_STATUS_OK bert_operation_id: "BERT-operation id 1" prbs_polynomial: PRBS_POLYNOMIAL_PRBS31 last_bert_start_timestamp: 1652379568178 last_bert_get_result_timestamp: 1652379688037 peer_lock_established: true error_count_per_minute: 0 total_errors: 0 } ================================= Response JSON: { "perPortResponses": [ { "interface": { "origin": "origin", "elem": [ { "name": "interfaces" }, { "name": "interface", "key": { "name": "et-1/0/2" } } ] }, "status": "BERT_STATUS_OK", "bertOperationId": "BERT-operation id 1", "prbsPolynomial": "PRBS_POLYNOMIAL_PRBS31", "lastBertStartTimestamp": "1652379568178", "lastBertGetResultTimestamp": "1652379688037", "peerLockEstablished": true, "errorCountPerMinute": [ 0 ], "totalErrors": "0" } ] }
BERT は正常に完了し、リンクの品質が良好であることを示しています。