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 は正常に完了し、リンクの品質が良好であることを示しています。