• 検索結果がありません。

NETCONF プロトコルの概要 NETCONF は YANG データモデルと共に使用される現在の主要なトランスポートプロトコルです これは NETWORK CONFIGURATION( ネットワークコンフィギュレーション ) プロトコルであり マネージャおよびエージェントが標準化された様式で通信す

N/A
N/A
Protected

Academic year: 2021

シェア "NETCONF プロトコルの概要 NETCONF は YANG データモデルと共に使用される現在の主要なトランスポートプロトコルです これは NETWORK CONFIGURATION( ネットワークコンフィギュレーション ) プロトコルであり マネージャおよびエージェントが標準化された様式で通信す"

Copied!
22
0
0

読み込み中.... (全文を見る)

全文

(1)

NETCONF プロトコルの概要

NETCONF は YANG データ モデルと共に使用される現在の主要なトランスポート プロトコルです。これはNETWORK CONFIGURATION(ネットワーク コンフィギュ レーション)プロトコルであり、マネージャおよびエージェントが標準化された様式で 通信する方法を定義するものです。 主な内容: • 2006 年に RFC4741 で最初に標準化されました • 最新の規格は 2011 年の RFC6241 です • 明示的にコンテンツを定義するものではありません。それは YANG によって 提供されるものです。

(2)

NETCONF にはトランスポート プロトコルとしての階層化アプローチがあります。 これはマネージャ(クライアント)とエージェント(サーバ)が通信する方法を定義する ものです。

(3)

トランスポート:

SSH

NETCONF はネットワークを介した通信のための基本的なメソッドとして SSH を利 用します。NETCONF の認証オプションは、すべての SSH 通信と同じです。CLI 通 信に SSH を使用するときのように、ユーザ名とパスワードまたは証明書を使用でき ます。実際に SSH クライアントを使用した NETCONF の例があります。 例 1:NETCONF の開始 SSH クライアントを利用して NETCONF デバイスに接続する例を示します。 $ ssh [email protected] -p 830 -s netconf [email protected]'s password: <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <capabilities> <capability>urn:ietf:params:netconf:base:1.1</capability> <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability> <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability> <capability>urn:ietf:params:xml:ns:yang:ietf-interfaces</capability>

[output omitted and edited for clarity] </capabilities> <session-id>19150</session-id></hello>]]>]]> デバイスで NETCONF エージェントへのログインが成功すると、サポートされる 「capabilities」のリストが返されます。次のステップは、XML でパッケージ化された NETCONF マネージャのサポートとしてふるまう「capabilities」を送信することで す。これは次のように表示されます。 <?xml version="1.0" encoding="UTF-8"?> <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <capabilities> <capability>urn:ietf:params:netconf:base:1.0</capability> </capabilities> </hello>]]>]]>

(4)

NETCONF の capabilities について NETCONF はトランスポート プロトコルですが、基本プロトコルの一部としてデバイ ス情報を設定または取得するための手段は提供しません。NETCONF は、YANG (および非 YANG)データ モデルを利用し、デバイスがどの「capabilities」を提供す るのかを詳述します。これらの capabilities のリストは、「開始」フェーズの一部とし て、マネージャとエージェント間における最初の通信中にやりとりされます。ここにも う一度基本的な SSH の例を挙げます。<capabilities> のリストに注意してく ださい。 $ ssh [email protected] -p 830 -s netconf [email protected]'s password: <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <capabilities> <capability>urn:ietf:params:netconf:base:1.1</capability> <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability> <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability> <capability>urn:ietf:params:xml:ns:yang:ietf-interfaces</capability>

[output omitted and edited for clarity] </capabilities>

<session-id>19150</session-id></hello>]]>]]>

capabilities のリストを調べることにより、開発者はどのデータを要求または設定で

きるのかがわかります。各 capabilities はデータ伝送中に使用される名前空間に

(5)

NETCONF の通信に ncclient を使用

この例のように NETCONF を SSH クライアントで直接使用することは可能です が、一般的ではありません。むしろ raw NETCONF 通信の送信および処理の詳細 を扱う、特別な目的のソフトウェア アプリケーションやプログラム ライブラリを使用 することが多いと思われます。 ニーズや言語選択によって開発者が利用可能な、その種のソフトウェアは数多く存 在します。このラボでは、ncclient を利用します。 これは、XML エンコードされた NETCONF の特性を Python に合わせてマッピン グする、直感的な API を提供することを目的とした Python ライブラリです。

これ以降、すべての NETCONF の例では ncclient および Python スクリプトを利

用します。 例 2:ncclient による開始 ターミナル インターフェイスを開きます。requirements とともにインストールされた Python 仮想環境がアクティブであることを確認し、このモジュール用のサンプル コード ディレクトリへ変更してください。 このラボ用のサンプル コードに含まれるのは、get_capabilities.py と呼ば

れる Python スクリプトです。python get_capabilities.py コマンドで実行

してください。

***Here are the Remote Devices Capabilities***

urn:ietf:params:xml:ns:yang:smiv2:DISMAN-EXPRESSION-MIB urn:ietf:params:xml:ns:yang:smiv2:MPLS-VPN-MIB urn:ietf:params:xml:ns:yang:smiv2:VPN-TC-STD-MIB urn:ietf:params:xml:ns:yang:smiv2:CISCO-SESS-BORDER-CTRLR-CALL-STATS-MIB urn:ietf:params:xml:ns:yang:smiv2:CISCO-RTTMON-TC-MIB urn:ietf:params:xml:ns:yang:smiv2:CISCO-AAA-SERVER-MIB urn:ietf:params:xml:ns:yang:smiv2:CISCO-EVC-MIB urn:cisco:params:xml:ns:yang:cisco-process-cpu urn:ietf:params:xml:ns:yang:smiv2:EtherLike-MIB urn:ietf:params:xml:ns:yang:ietf-netconf-acm

(6)

urn:cisco:params:xml:ns:yang:cisco-cfm-stats http://tail-f.com/yang/acm

urn:ietf:params:netconf:base:1.0

urn:ietf:params:xml:ns:yang:smiv2:TUNNEL-MIB

urn:ietf:params:xml:ns:yang:smiv2:CISCO-MPLS-LSR-EXT-STD-MIB [output truncated in lab]

長い出力リストが出てくるはずです。リストの各行は、CSR 上で NETCONF エー ジェントによってサポートされる「capabilities」を表します。この「capabilities」とは、 単に、サポートされるデータ モデルの参照であることを覚えておいてください。この リスト全体を見ると、名前の多くに「ietf:params:xml:ns:yang」が含まれていること がわかると思います。これらはサポートされている IETF 標準の YANG モデルを示 します。 XML 内で capabilities がどのように「名前空間」として参照されているかがすぐに わかります。 get_capabilities.py ファイルを開き、開発者が ncclient によって簡単に NETCONF を扱う方法を見てみましょう。 #!/usr/bin/env python

from ncclient import manager import sys

# the variables below assume the user is leveraging the # network programmability lab and accessing csr1000v # use the IP address or hostname of your CSR1000V device

HOST = '198.18.133.218'

# use the NETCONF port for your CSR1000V device

PORT = 2022

# use the user credentials for your CSR1000V device

USER = 'admin' PASS = 'C1sco12345'

(7)

# create a main() method

def main(): """

Main method that prints netconf capabilities of remote device. """

with manager.connect(host=HOST, port=PORT, username=USER, password=PASS, hostkey_verify=False, device_params={'name': 'default'},

look_for_keys=False, allow_agent=False) as m: # print all NETCONF capabilities

print('***Here are the Remote Devices Capabilities***') for capability in m.server_capabilities:

print(capability.split('?')[0]) if __name__ == '__main__': sys.exit(main()) では、このコードの動作を見てみましょう。 • まず、ncclient および sys ライブラリをインポートします。 o 自分のコンピュータで作業している場合は、このコードを実行する前 に、NCClient ライブラリをインストールする必要があります。 o Python3 との互換性を確保するために、最低でも ncclient バー ジョン 0.5.2 を使用する必要があります。 • 次の行は複数の名前を作成します。これらの名前は、環境に合わせて必要 に応じて更新できます。 o ラボ環境で CSR1000V を使用している場合は、何も変更する必要は ありません。 o このコードを実行する前に、ラボ環境に接続していることを確認してくだ さい。 o 必要に応じてモジュール 03 の手順を確認してください。

(8)

• このコード スニペットは、以下のように、必要な引数を使用して NETCONF over SSH セッションを作成します(空白は読みやすさのために追加されてい ます)。

with manager.connect(host=HOST, port=PORT, username=USER, password=PASS, hostkey_verify=False, device_params={'name': 'default'}, allow_agent=False, look_for_keys=False) as m: o host = リモート デバイスの IP アドレスまたはホスト名。 o port = SSH セッション用の NETCONF ポート。 o username = SSH セッションを認証するためのユーザ名。 o password = SSH セッションを認証するためのパスワード。 o hostkey_verify = ~/.ssh/known_hosts からホストキーを無効に します。 o device_params = ベンダー固有の operations を有効にします(こ の例では特殊な operations は行いません)。 o look_for_keys = ユーザ名/パスワードを使用しているため、公開 キーを無効にします。 o allow_agent = ユーザ名/パスワードを使用しているため、公開キー を無効にします。 o with ... as 文を使用することにより、ランタイム時に何らかの例外 が発生した場合、セッションは正常に終了されます。 • 接続後、変数m が NETCONF セッションを表します。セッションには m.server_capabilities と呼ばれるプロパティが存在します。これには 接続の手順の間に返された capabilities の詳細が含まれます。 o 基本的な for ... in ループを使用してリストを出力します。

o for capability in m.server_capabilities: o print(capability.split('?')[0])

• 最後に、if __name__ == '__main__': という文があります。

o これにより、スクリプトが直接呼び出された場合(モジュールとしてイン

ポートされた場合ではなく)にのみ、main() メソッドが実行されるよう

(9)

セキュリティに関する考慮事項 このラーニング ラボの例では、公開キーを使用するのではなく、ユーザ名/パスワー ドを Python スクリプトに直接挿入しています。 USER = 'admin' PASS = 'C1sco12345' さらに、このラーニング ラボの例では、hostkey_verify=False を使用して、 known_hosts ファイルを無視しています。

with manager.connect(host=HOST, port=PORT, username=USER, password=PASS, hostkey_verify=False, device_params={'name': 'default'}, look_for_keys=False, allow_agent=False) as m:

実稼働環境では、このような設定を使用しないでください。

これらの設定と全体的なアプローチは、ラボのテスト環境の簡単な構築と実行に便 利です。

(10)

メッセージ:リモート

プロシージャ コール(RPC)

NETCONF で送信されたメッセージはリモート プロシージャ コール(RPC)を利用し ます。RPC はクライアントがサーバへ「リモートで」アクションを実行して結果を提供 することを要求する標準フレームワークです。 NETCONF での実際の目的が意味するものは、マネージャからのそれぞれの通信 が <rpc> の XML タグでラップされ、その結果が <rpc-reply> の XML タグで ラップされて返されるということです。それらのタグには message-id 属性があり、 それらを結び付けています。次に例を示します。 <rpc-reply message-id="urn:uuid:7db557ee-ed4c-456c-a058-2d69bd4228c4"> ・ ・ </rpc-reply> XML のペイロードを手動で作成すると、RPC 情報を管理するのは作成者の責任に なります。ただし、NETCONF 用の ncclient などのクライアントを使用することによ り、RPC エレメントの大半は作成者用に処理されます。

(11)

Operation:NETCONF のアクション

エージェントへのリクエストを作成するとき、NETCONF ではいくつかのアクションが あります。アクションは XML タグを使用して示されます。それらは次のとおりです。 Operation 説明 <get> 実行中のコンフィギュレーションおよびデバイスの状態情報を取得する <get-config> 指定されたコンフィギュレーションは一部を取得する データストアの全体また <edit-config> 指定されたコンフィギュレーション設定または一部の設定をロードする データストアにすべての <copy-config> コンフィギュレーション データストア全体を他で置き換える <delete-config> コンフィギュレーション データストアを削除する <commit> 実行中のデータストアに候補のデータストアをコピーする <lock> / <unlock> コンフィギュレーションまたはロック解除する データストア システム全体をロック <close-session> NETCONF セッションの段階的終了 <kill-session> NETCONF セッションの強制終了 例 3:デバイスのホスト名を取得 ターミナル インターフェイスを開きます。requirements とともにインストールされた Python 仮想環境がアクティブであることを確認し、このモジュール用のサンプル コード ディレクトリへ変更してください。 このラボ用のサンプル コードには get_hostname.py と呼ばれるファイルが含ま れます。この Python スクリプトでは NETCONF を利用してデバイスに接続し、 YANG データ モデルを使用して自身の「ホスト名」を決定します。python get_hostname.py コマンドでコードを実行してください。このような出力が返され るはずです。

(12)

csr-nwp1

スクリプトを開くと、operation を使用してこの情報を取得する方法を確認することが

できます。

#!/usr/bin/env python

# import the ncclient library

from ncclient import manager import sys

import xml.dom.minidom

# the variables below assume the user is leveraging the # dCloud network programmability lab.

#

# use the IP address or hostname of your CSR1000V device

HOST = '198.18.133.218'

# use the NETCONF port for your CSR1000V device

PORT = 2022

# use the user credentials for your CSR1000V device

USER = 'admin' PASS = 'C1sco12345'

# create a main() method

def main(): """

Main method that retrieves the hostname from config via NETCONF. """

with manager.connect(host=HOST, port=PORT, username=USER, password=PASS, hostkey_verify=False, device_params={'name': 'default'},

(13)

# XML filter to issue with the get operation hostname_filter = ''' <filter> <native xmlns="urn:ios"> <hostname></hostname> </native> </filter> '''

result = m.get_config('running', hostname_filter) xml_doc = xml.dom.minidom.parseString(result.xml) hostname = xml_doc.getElementsByTagName("hostname") print(hostname[0].firstChild.nodeValue) if __name__ == '__main__': sys.exit(main()) このスクリプトの多くは、capabilities を取得した際の前例と一致します。ここでは新 しい情報に注目します。 • import xml.dom.minidom で追加のライブラリをインポートしています。 これにより、Python コードで XML を使用する作業がより簡単になります。 • まったく同じコードおよびプロセスを使用して接続します。 • ここではhostname_filter を作成します。 • hostname_filter = ''' <filter> <native xmlns="urn:ios"> • <hostname></hostname> • </native> </filter> ''' o これは「get-config」動作の範囲を制限するために使用する XML 文字 列です。このフィルタを通さない場合、デバイスは全体のコンフィギュ

(14)

レーションを返します。長く複雑なコンフィギュレーションのデバイスや システムでは、これは不要なオーバーヘッドになります。

• <native> タグ内の xmlns="urn:ios" に注意してください。

o これは、フィルタがターゲットにしている「XML 名前空間」を識別します

(名前空間について詳しくは追って説明します)。

o 「urn:ios」は CSR がサポートする capabilities の 1 つですが、「ietf」 がないことに気付くと思います。その理由は、「標準」モデルではなく、 シスコによって作成されたベンダー(「ネイティブ」)モデルを参照してい るからです。 o 名前空間の「YANG」について言及されていませんが、「urn:ios」は、 YANG データ モデルです。 • 接続およびフィルタが利用可能な場合、result =

m.get_config('running', hostname_filter) は「get-config」

operation を発行する方法です。 o 関数の最初のパラメータ「running」は、ターゲットになっているデータ ストアを参照します。「データ ストア」については後で詳しく説明します。 • m.get_config() メソッドは XML ペイロードを返し、次の行セットで XML Python ライブラリを使用してコンテンツを処理し、「ホスト名」の値を出力します。 • xml_doc = xml.dom.minidom.parseString(result.xml) hostname = xml_doc.getElementsByTagName("hostname") print(hostname[0].firstChild.nodeValue)

(15)

NETCONF データ ストア

IETF が SNMP の代替について検討している際に特定された重要な要件の 1 つ は、コンフィギュレーションの検証、エラー チェックや処理、およびロールバックのた めの統合されたメソッドでした。 このニーズに対応するものの 1 つとして、NETCONF には個別の operation イベ ントのターゲットを提供する「データ ストア」があります。各コンテナは、アクティブな コンフィギュレーションにコミットする前に事前検証が可能な、設定データのコピーを 保持します。 最後の例では、このコード ラインの「running」データ ストアに注目します。

result = m.get_config('running', hostname_filter)

データ ストアの別の利点は、ネットワーク管理システムによってネットワーク内のす べてのデバイスの設定がデータ ストア内で一貫していることを検証してから、ネット ワークにおける全体的な変更を一度に実行できることです。 データ ストアのキー ポイント • コンテナは全体または一部の設定を保持できます • すべてのデータ ストアがすべてのデバイスでサポートされるわけではありま せん o 「running」は唯一の必須データ ストアです • すべてのデータ ストアが書き込み可能ではありません

(16)

• 「URL」のデータ ストアは<config-copy> を有効にするため IOS によって サポートされます

• すべての NETCONF のメッセージはデータ ストアをターゲットにする必要が

(17)

コンテンツ:コンフィギュレーションまたはオペレーション データ

NETCONF で送受信されたデータは XML でエンコードされ、

<data>...</data> タグの間に含まれます。含まれるデータ モデルの詳細は、

名前空間を参照することによって提供されます。作業するデータのほとんどにおい て名前空間は YANG モデルを参照しますが、非 YANG データも NETCONF で送 信可能です。 XML タグの一部である属性 xmlns を参照することにより、データの名前空間を特定できます。 例 4:ietf インターフェイスのクエリ 端末インターフェイスを開きます。要件とともにインストールされた Python 仮想環 境がアクティブであることを確認し、このモジュール用にサンプル コード ディレクトリ を変更してください。 このラボ用のサンプル コードには、get_interfaces_config.py と呼ばれる ファイルが含まれます。このスクリプトは、ncclient を利用して「ietf インターフェイ ス」モデルに基づく設定の詳細を取得します。コマンド python get_interfaces_config.py でこのスクリプトを実行します(同様のスクリプト は YANG の基本ラボでも使用)。 <?xml version="1.0" ?>

<rpc-reply message-id="urn:uuid:823b5318-248a-4247-aa74-c94eda2fd9cf"

xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> <data> <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> <interface> <name>GigabitEthernet1</name>

<type xmlns:ianaift= "urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"> <address> <ip>198.18.133.212</ip> <netmask>255.255.192.0</netmask> </address>

(18)

</ipv4>

<ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface>

<interface>

<name>GigabitEthernet3</name>

<type xmlns:ianaift= "urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> <enabled>false</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> </interfaces> </data> </rpc-reply> NETCONF の出力を確認 スクリプト自体もざっと確認しますが、まず返されたデータを見てみましょう。応答の 完全な XML ペイロードが表示されているためです。  最初の行は、確かに XML であり、バージョン 1.0 を使用していることを示し ます。  先に学習したように、NETCONF は一貫して RPC メッセージを使用します。 ここでの 2 行目と最終行で <rpc-reply> ... を確認していることがわか ります</rpc-reply>  Withinrpc-reply には、関心のある情報の一部を含む <data> ...</data> タグがあります。  名前空間の説明に戻り、詳細のブロックを開く行 <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> に注目します。 o これにより、開始タグ <interfaces> および終了タ グ </interface> で挟まれているものは、すべ て urn:ietf:params:xml:ns:yang:ietf-interface の名 前空間でフォーマットされていることがわかります。  長い文字列はこの出力で使用された特定のデータ モデルを示 します。これは ietf-interface と呼ばれる YANG モデル用の

(19)

IETF によって提供される Universal Resource Name として、 大まかに解釈することができます。  データ モデル(名前空間)は相互にネストできます。行 <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> を見てみましょう。 o ここでは、インターフェイス タイプに独自の名前空間(および YANG データ モデル)があることがわかります。 コードの確認 では、get_interfaces_config.py ファイルを開きます。 #!/usr/bin/env python #

# Get configured interfaces using Netconf #

# [email protected] #

from ncclient import manager import sys

import xml.dom.minidom

# the variables below assume the user is leveraging the # network programmability lab and accessing csr1000v # use the IP address or hostname of your CSR1000V device HOST = '198.18.133.218'

# use the NETCONF port for your CSR1000V device PORT = 2022

# use the user credentials for your CSR1000V device USER = 'admin'

PASS = 'C1sco12345'

# XML file to open

(20)

# create a main() method

def get_configured_interfaces(xml_filter):

"""

Main method that retrieves the interfaces from config via NETCONF. """

with manager.connect(host=HOST, port=PORT, username=USER, password=PASS, hostkey_verify=False, device_params={'name': 'default'},

allow_agent=False, look_for_keys=False) as m: with open(xml_filter) as f:

return(m.get_config('running', f.read()))

def main(): """

Simple main method calling our function. """ interfaces = get_configured_interfaces(FILE) print(xml.dom.minidom.parseString(interfaces.xml).toprettyxml()) if __name__ == '__main__': sys.exit(main()) このスクリプトは前に見た 2 つと非常によく似ています。ここではその違いを確認し てみましょう。  まずは、スクリプト内で XML 文字列を作成してフィルタを保持するのではな く、このスクリプト ブロック内の個別のファイルからそれを読み取っています。  with open(xml_filter) as f:

(21)

o XML フィルタは極めて長くなる場合があり、異なるスクリプトによって 何度も参照される可能性があります。個別のファイルで保存することに よってコード クリーナーが作成され、維持しやすくなります。 o サンプル コード ディレクトリに含まれる get_interfaces.xml を 開きます。 o <filter> o <interfaces xmlns= "urn:ietf:params:xml:ns:yang:ietf-interfaces"> o <interface></interface> o </interface> o </filter> XML および NETCONF のまとめ NETCONF および YANG を効果的に活用するための重要なスキルは、プロトコル の中核である XML のコンテンツの構築と読み取りです。YANG モデルを調査し、 それらを XML フィルタ内で作成して必要なデータを取得できるようになることは、こ れらのテクノロジーをより一層発展させるための重要なスキルです。

(22)

ラボの確認:

NETCONF を使用する際の手順

NETCONF の重要な要素についての正しい理解を踏まえて、開発者がそれを使用 する際のプロセスについて、順を追って見てみましょう。 1. NETCONF Manager を使用してデバイスのエージェントに接続し、 <hello> で開始 2. エージェントが <capabilities> のリストで応答 3. 開発者は capabilities として提供された利用可能なデータ モデルを調査し、 自身のニーズを最も満たすものを 1 つ選択 4. 使用可能な operation を使用して送信される XML データを構成(例: <get-config>) 5. マネージャからリモート プロシージャ コール <rpc> としてメッセージを送信 6. エージェントは <rpc-reply> を返信 7. 応答に含まれる <data> を処理 また、Python 用の ncclient などのツールを利用することで、開発者はプロトコルの 複雑さを気にせずアプリケーション開発に集中できます。

参照

関連したドキュメント

7IEC で定義されていない出力で 575V 、 50Hz

仏像に対する知識は、これまでの学校教育では必

2021] .さらに対応するプログラミング言語も作

これはつまり十進法ではなく、一進法を用いて自然数を表記するということである。とは いえ数が大きくなると見にくくなるので、.. 0, 1,

つの表が報告されているが︑その表題を示すと次のとおりである︒ 森秀雄 ︵北海道大学 ・当時︶によって発表されている ︒そこでは ︑五

定的に定まり具体化されたのは︑

□ ゼミに関することですが、ゼ ミシンポの説明ではプレゼ ンの練習を主にするとのこ とで、教授もプレゼンの練習

必要があります。仲間内でぼやくのではなく、異