このページの内容
gNOI診断(診断)サービス
gNOI診断(Diag)サービスを使用して、2つのデバイス間のリンクの信頼性をテストします。
概要
DiagサービスRPCを使用して、接続されたポートのペアでビットエラーレートテスト(BERT)を実行します。Diagサービスプロト定義ファイルは、https://github.com/openconfig/gnoi/blob/master/diag/diag.proto にあります。
BERTは、擬似ランダムバイナリシーケンス(PRBS)テストとも呼ばれ、リンクの信頼性をテストします。 StartBERT() gNOI RPCは、接続された物理インターフェイスのペアで双方向BERTを開始します。デバイスは、リンク全体で1と0の設定されたパターンを交換します。デバイスは受信したメッセージと送信したメッセージを比較し、エラーの数をカウントします。エラー数が少ないほど、リンクの品質は高くなります。
デバイスが結果を比較できるように、リンクの両側でgNOI BERTを実行する必要があります。テストしているリンクは、BERTの実行中にダウンし、BERTの終了後に再び立ち上がります。ただし、BERTを実行しているデバイスの1つが再起動すると、もう一方のデバイスでBERTを停止しない限り、リンクはダウンしたままになります。
テストパターンは、いくつかの所定のタイプから選択できます。BERTまたはPRBSテストパターンのタイトルは、PRBSxの形式で、xは数値です。Junosデバイスは、gNOI BERTの以下のテストパターンをサポートしています。
- 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のすべてのリクエスト結果を表示するには、GetBERTResultRequestメッセージのresult_from_all_portsフィールドをTrueに設定します。
デバイス上で GetBERTResult() RPCを実行すると、RPCは、特定のデバイスがBERT中に検出した不一致ビットの数を表示します。RPCには合格または不合格の基準が設定されていないため、結果を評価するのはユーザー次第です。以下のような理由により、多数のエラーが発生することがあります。
- リンクの品質が悪い。
- BERT中にデバイスの1つがオフラインになりました。
- BERTは1台のデバイスでのみ実行されました。
- BERTは、両方のデバイスで同時に起動および停止しませんでした。
最後のエラーを回避するために、 StartBERT() RPCを両方のデバイスに同時に送信することをお勧めします。一方のデバイスで他方のデバイスよりも先にBERTを開始した場合、もう一方のデバイスでBERTが起動するまで、最初のデバイスは応答を受信しません。最初のデバイスは、応答の欠如を不一致ビットとして記録します。2番目のデバイスでBERTが起動するまで、最初のデバイスはエラーを報告し続けます。BERTを同時に起動できない場合は、最後にBERTを開始したデバイスで GetBERTResult() RPCを実行することをお勧めします。BERTはすでに最初のデバイスで実行されていたため、2番目のデバイスは誤った欠落ビットを報告しません。
サポートされているRPC
| RPC | 説明 | リリースで導入 |
|---|---|---|
StartBERT() |
一連のポートでBERTを開始します。Junosデバイスは、gNOI BERTに対して以下のPRBSパターンをサポートしています。
|
Junos OS Evolved 22.2R1 |
StopBert() |
一連のポートですでに進行中のBERTを停止します。 |
Junos OS Evolved 22.2R1 |
GetBERTResult() |
BERT中またはBERT完了後にBERT結果を取得します。 |
Junos OS Evolved 22.2R1 |
ネットワークデバイスの設定
始める前に:
- gRPCサービスを設定するの説明に従って、ネットワークデバイス上でgRPCサービスを設定します。
- gNOIサービスを設定するで説明されているように、gNOI操作をサポートするようにネットワーク管理システムを構成します。
- BERTを実行するリンクについて、サーバーとピアインターフェイスの速度を一致するように設定します。BERTは、インターフェイスの速度が一致する場合にのみ実行されます。
例:BERTを実行する
インターフェイス上でBERTが進行中は、そのインターフェイス上の物理リンクがダウンします。
gNOI クライアントとサーバーを設定したら、BERT を実行するためのアプリケーションを作成して実行する準備が整います。この例では、クライアントが gnoi_bert_client.py Pythonアプリケーションを実行して、サーバーとピアデバイス間のリンクをテストします。 gnoi_bert_client.py アプリケーションは、引数に応じて、BERTの開始、BERTの停止、またはBERT結果の取得を行うことができます。
まず、クライアントは gnoi_bert_client.py を使用して StartBERT() RPC を送信し、サーバーとピアで BERT を開始します。BERTの実行中、サーバーとピアは、et-1/0/2とet-1/0/3インターフェイス間のリンクを介してBERTテストパケットを交換します。
時のネットワークトポロジー
BERTは、設定された時間が終了すると終了します。次に、クライアントは GetBERTResult() RPC でアプリケーションを 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は、両方のJSONファイルでBERT-operation id 1されています。
アプリケーションは、チャネルを確立するための 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=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.')
# 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を開始するには、messageをStartBERTRequestに設定し、input_fileをinput_bert_start.jsonファイルパスに設定します。各デバイスについて、入力ファイルには、そのデバイスでテストされたインターフェイスを指定する必要があります。BERT_STATUS_OKステータスは、BERTが正常に開始されたことを示しています。lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_bert_client.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 --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が終了したら、
messageをGetBERTResultRequestに設定し、input_fileをinput_bert_get.jsonファイルパスに設定して、gnoi_bert_client.pyアプリケーションを再度実行し、テストの結果を取得します。この例では、BERTは1分間のテスト中にエラーを検出しませんでした。lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_bert_client.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 --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は正常に完了し、リンクの品質が良好であることを示しています。