CLIプロキシ

CLIプロキシサービスは、ネットワーク機器やサーバに対するCLI制御をAPI化する機能を提供しています。
一つのCLIセッションで実行する一連のコマンド送受信を定義します。また送信コマンドは、Jinja2テンプレート形式で記述することができます。
コマンドテンプレートへのレンダリングパラメータは、実行時に与えることができますが、デバイスから応答された受信結果からパラメータを生成して次のコマンドテンプレートに適用することができます。

CLIプロキシの定義項目

category: CLIプロキシのカテゴリを指定します。カテゴリは単なるラベルです。

name: CLIプロキシの名前を指定します。ユニークである必要があります。

path: CLIプロキシが生成するAPI待ち受けパスを指定します。ユニークである必要があります。自動生成されるAPIは全てPATCHメソッドのみを受け付けます。

protocol: CLIアクセスで利用するプロトコルを指定します。telnetもしくはsshのみ指定できます。

async: CLIプロキシが生成するAPIの非同期モードを指定します。Trueの場合、APIを要求した場合は、CLIプロキシを起動するlambdaイベントを発行してイベントIDを応答します。Falseの場合、lambdaイベントの発行後、イベント収束まで待ち合わせて処理結果を応答します。

timeout: CLIプロキシの実行によって生成されるlambdaイベントの収束タイムアウト時間を指定します。デフォルトは、300秒です。

config: CLIプロキシがコマンド送信する際にコンフィグモードで実行するかを指定します。Ciscoデバイスの場合は、開始時にconfig term、終了時はendが自動送信されます。デフォルトはFalseです。

templates: CLIプロキシが対象デバイスに対して送受信するコマンドや実行条件などを記述する定義ブロックです。

templates.command: 送信するコマンドを記述します。コマンドは、Jinja2テンプレート形式で記述できます。レンダリングパラメータは、コマンド送信時点のbindings辞書が使用されます。

templates.execution_condition: コマンドの送信可否を判定するカスタムスクリプトを記述します。例えば、version in ["1.0", "1.1"]と記述した場合、bindingsに存在するversion変数が1.0もしくは1.1の場合のみコマンドが送信されます。

templates.fault_condition: コマンドの送信時点のbindings辞書に対してCLIプロキシの処理に失敗がないか判定するカスタムスクリプトを記述します。例えば、version not in ["1.0", "1.1"]と記述した場合、bindingsに存在するversion変数が1.0もしくは1.1でない場合、CLIプロキシは中断してエラーを返却します。後述するtemplates.rollbackが定義されている場合は、ロールバック動作に遷移します。

templates.prompt_terminator: コマンド送信後に待ち受けるプロンプトを指定します。通常は自動検出されます。

templates.timeout: コマンド送信のタイムアウトを指定します。デフォルトは、起動パラメータのssh_default_timeoutです。起動パラメータのデフォルトは60秒です。

templates.expect_regexes: コマンド送信後にデバイスから受信した応答メッセージに対して期待する正規表現を記述します。全ての正規表現にマッチした場合、正常と見なし、次のコマンド送信判定に遷移します。いづれかでもマッチしない場合、処理を中断してエラーを返却します。後述するtemplates.rollbackが定義されている場合は、ロールバック動作に遷移します。

templates.unexpect_regexes: コマンド送信後にデバイスから受信した応答メッセージに対して期待しない正規表現を記述します。いづれかの正規表現にマッチした場合、異常と見なし、処理を中断してエラーを返却します。後述するtemplates.rollbackが定義されている場合は、ロールバック動作に遷移します。

templates.rollback: CLIプロキシが何らかのエラーによって処理を中断した場合に実行されるロールバック処理を記述する定義ブロックです。

templates.rollback.command: ロールバックで送信するコマンドを記述します。templates.command同様にJinja2テンプレート形式で記述できます。

templates.rollback.prompt_terminator: ロールバックコマンド送信後に待ち受けるプロンプトを指定します。通常は自動検出されます。

templates.rollback.precondition: templates.rollback.commandの送信前に実行可否を判定するためのshowコマンドを送信したい場合に指定できる定義ブロックです。

templates.rollback.precondition.command: 実行可否判定のために送信するコマンドを記述します。templates.command同様にJinja2テンプレート形式で記述できます。

templates.rollback.precondition.prompt_terminator: 実行可否判定コマンド送信後に待ち受けるプロンプトを指定します。通常は自動検出されます。

templates.rollback.precondition.expect_regexes: 実行可否判定コマンドの応答結果に期待する正規表現を記述します。いづれかでもマッチしない場合、ロールバックは失敗とみなされエラーを返却します。

templates.rollback.precondition.unexpect_regexes: 実行可否判定コマンドの応答結果に期待しない正規表現を記述します。いづれかでもマッチする場合、ロールバックは失敗とみなされエラーを返却します。

templates.rollback.postcondition: templates.rollback.commandの送信後にロールバックの継続可否を判定するためのshowコマンドを送信したい場合に指定できる定義ブロックです。

templates.rollback.postcondition.command: ロールバック継続可否判定のために送信するコマンドを記述します。templates.command同様にJinja2テンプレート形式で記述できます。

templates.rollback.postcondition.prompt_terminator: ロールバック継続可否判定コマンド送信後に待ち受けるプロンプトを指定します。通常は自動検出されます。

templates.rollback.postcondition.expect_regexes: ロールバック継続可否判定コマンドの応答結果に期待する正規表現を記述します。いづれかでもマッチしない場合、ロールバックは失敗とみなされエラーを返却します。

templates.rollback.postcondition.unexpect_regexes: ロールバック継続可否判定コマンドの応答結果に期待しない正規表現を記述します。いづれかでもマッチする場合、ロールバックは失敗とみなされエラーを返却します。

templates.parameter_bindings: CLIプロキシの一連のコマンド処理でデバイスから受信した応答メッセージからパラメータを導出する処理を記述する定義ブロックです。

templates.parameter_bindings.name: 導出するパラメータの変数名を指定します。

templates.parameter_bindings.regex: 導出するパラメータに代入するための値としてデバイスの応答メッセージからテキストを抜き出す正規表現を指定します。

templates.parameter_bindings.script: 導出するパラメータに代入するための値としてデバイスの応答メッセージからテキストを抜き出すカスタムスクリプトを記述します。カスタムスクリプトの実行空間には、デバイスの応答メッセージが代入されたoutput変数が格納されています。output変数を参照してカスタムスクリプトで生成した値をv変数に代入するとtemplates.parameter_bindings.nameで指定したbindings辞書のパラメータに値がセットされます。


aop: CLIプロキシの実行前後に実行したい同期型のカスタムスクリプトを埋め込むことができるオプションです。

aop.pre: CLIプロキシの実行前に実行するカスタムスクリプトを記述します。CLIプロキシの実行前にbindings情報を加工したり、新たなbindingsパラメータを生成することができます。カスタムスクリプトの実行空間には、開始時点のbinginsパラメータがデフォルトでセットされています。

aop.post: CLIプロキシの実行後に実行するカスタムスクリプトを記述します。CLIプロキシの実行によって確定した最終的なbindings情報を加工したり、新たなbindingsパラメータを生成することができます。カスタムスクリプトの実行空間には、bindingsパラメータがデフォルトでセットされています。

Note

aopオプションの使い方は少し難解です。CLIプロキシ呼び出し時に全てのレンダリングパラメータを受け取る場合やCLIプロキシのコマンド送受信で生成した結果パラメータだけで事足りる場合は通常使用することはありません。呼び出し時に指定されたパラメータやCLIプロキシの処理完了時に生成されたパラメータを基に新たなパラメータを導出してコマンド送信時にレンダリングしたり、処理結果(bindings)に追加したい場合などに使用します。

  • AOP定義サンプル

以下のCLIプロキシサンプルは、grepというパラメータを与えて実行すると事前カスタムスクリプトによってfilterConditionというパラメータが生成されます。その後、コマンド送信が実行され、コマンドの応答からversionパラメータが生成されます。最後に事後カスタムスクリプトによってtextVersionというパラメータが生成されます。

- name: showVersion
  path: /showVersion
  protocol: ssh
  templates:
    - command: 'show ver {{ filterCondition }}'
      parameter_bindings:
        - name: version
          regex: '\d{2}\.\d{2}\.\d{2}\.S'
  aop:
    pre: 'filterCondition = "| inc (Cisco IOS XE Software, Version)" if grep is True else ""'
    post: textVersion = "v"+version


  • AOP実行例

templates.commandはCLIで送信するコマンドのテンプレートです。レンダリングパラメータはCLIプロキシを呼び出す際にrequiredキーとなります。この例ではfilterConditionが該当しますが、このパラメータは、aop.preによって値が生成されるため、CLIプロキシ呼び出し時は、キーのみ指定して値はNoneで構いません。

>>> r = await callout(path="/showVersion?alias=csr1000v", method=PATCH, body={"grep": True, "filterCondition": None})↵
... ↵
Connecting...
Done.
csr1000v#
show ver | inc (Cisco IOS XE Software, Version)
Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
csr1000v#

Qmonus> version = '03.15.00.S'
↵
disable
csr1000v>

Disconnecting...
Done.
>>> print(MU(json.loads(r.body)).yaml_format)↵
... ↵
↵
csr1000v:
  code: 200
  output:
    bindings:
      $alias: csr1000v
      $host: 192.168.2.200
      $password: qmonus
      $port: '22'
      $secret: qmonus
      $username: qmonus
      filterCondition: '| inc (Cisco IOS XE Software, Version)'
      grep: true
      textVersion: v03.15.00.S
      version: 03.15.00.S
    message: null
    responses:
    - |-
      show ver | inc (Cisco IOS XE Software, Version)
      Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
      csr1000v#
    status: Complete
>>>


チュートリアル

はじめてのCLIプロキシ

Cisco CSR1000vにSSH接続してバージョン情報を取得するサンプルです。
実行するコマンドは、show ver | inc (Cisco IOS XE Software, Version)のみでバージョン情報が出力される行に絞って実行しています。
parameter_bindingsでは、正規表現でバージョン番号だけを抜き出してversion変数に代入しています。

name: showVersion
path: /showVersion
protocol: ssh
templates:
  - command: 'show ver | inc (Cisco IOS XE Software, Version)'
    parameter_bindings:
      - name: version
        regex: '\d{2}\.\d{2}\.\d{2}\.S'


CLIプロキシを実行するためには、Deviceオブジェクトが必要です。Deviceオブジェクトは、Qmonus SDKのインメモリストアに永続化されます。Docs » Lambda » Deviceを参考にしてください。
Deviceオブジェクトは以下のパラメータを定義する必要があります。

alias: csr1000v
class: simplex
device: cisco_xe
host: 192.168.2.200
port: '22'
username: qmonus
password: qmonus
secret: qmonus
parameters: {}

Warning

Deviceオブジェクトには、usernamepasswordsecretといったシークレット情報が含まれる為、Qmonus SDKが吐き出すプラグイン定義ファイルでは、これらのデータは暗号化される点に注意してください。
暗号化及び復号化は、Qmonus SDKの起動パラメータplugin_secret_keyが利用されます。必ず開発プロジェクト単位に払い出して設定してください。


Deviceオブジェクトを作成したら、CLIプロキシを実行できます。実行する方法は、CLIProxy組込みオブジェクトを利用する方法と生成されたAPIをリクエストする方法の2種類が提供されます。
まずは、CLIProxy組込みオブジェクトを利用して実行してみましょう。CLIProxy組込みオブジェクトの使用方法は、Docs » リファレンス » ビルトインオブジェクトにも記載していますので参考にしてください。

>>> p = await CLIProxy.load("showVersion")↵
... r = await p.run("csr1000v")↵
... ↵
↵

csr1000v#

csr1000v#terminal length 0
terminal length 0
csr1000v#terminal width 511
terminal width 511
csr1000v#

csr1000v#

csr1000v#show ver | inc (Cisco IOS XE Software, Version)
show ver | inc (Cisco IOS XE Software, Version)
Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
csr1000v#
>>> print(r.bindings.version)↵
... ↵
↵
03.15.00.S
>>>


最初の実行例では、Deviceオブジェクトを作成し、そのaliasを指定して実行しましたが、Deviceオブジェクトを永続化したくない(インメモリストアでプラグインとしてDeviceを管理せず、リレーショナルデータベースで独自に管理したい)場合、以下のようにDeviceインスタンスを使って実行することもできます。

>>> device = Device("csr1000v", device="cisco_xe", host="192.168.2.200", username="qmonus", password="qmonus")↵
... p = await CLIProxy.load("showVersion")↵
... r = await p.run(device)↵
... ↵
↵
csr1000v#

csr1000v#terminal length 0
terminal length 0
csr1000v#terminal width 511
terminal width 511
csr1000v#

csr1000v#

csr1000v#show ver | inc (Cisco IOS XE Software, Version)
show ver | inc (Cisco IOS XE Software, Version)
Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
csr1000v#
>>> print(r.bindings.version)↵
... ↵
↵
03.15.00.S
>>>


最後に自動生成されたAPIをリクエストしてCLIプロキシを実行します。生成されるAPIの待受メソッドは、PATCHです。リクエスト本文でコマンドテンプレートへのレンダリングパラメータを渡すこともできます。

Note

CLIプロキシを登録した際にAPI GatewayのAPIルーティングが自動追加されます。

>>> r = await callout(path="/showVersion?alias=csr1000v", method=PATCH)↵
... ↵
Connecting...
Done.
csr1000v#
show ver | inc (Cisco IOS XE Software, Version)
Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
csr1000v#

Qmonus> version = '03.15.00.S'
↵
disable
csr1000v>

Disconnecting...
Done.
>>> print(MU(json.loads(r.body)).yaml_format)↵
... ↵
↵
csr1000v:
  code: 200
  output:
    bindings:
      $alias: csr1000v
      $host: 192.168.2.200
      $password: qmonus
      $port: '22'
      $secret: qmonus
      $username: qmonus
      version: 03.15.00.S
    message: null
    responses:
    - |-
      show ver | inc (Cisco IOS XE Software, Version)
      Cisco IOS XE Software, Version 03.15.00.S - Standard Support Release
      csr1000v#
    status: Complete
>>>


CLIプロキシによるコンフィグ例

interface設定のサンプルです。interface名と設定するIPアドレスとネットマスクは引数で指定できるようにします。
interfaceを活性化した後、showコマンドで状態を取得します。状態は、showコマンドの出力結果からカスタムスクリプトで送信コマンドと受信プロンプト部を除去して抽出しています。

- config: true
  name: config
  path: /config
  protocol: ssh
  templates:
    - command: 'interface {{ iface }}'
    - command: 'ip address {{ address }} {{ netmask }}'
    - command: no shutdown
    - command: 'do show ip interface brief | inc {{ iface }}'
      parameter_bindings:
        - name: status
          script: |-
            output = '\n'.join(output.split('\n')[1:-1])
            v = output.split()[-1] if output.split()[1]==address else None


  • REPLでの動作確認
>>> p = await CLIProxy.load("config")↵
... r = await p.run("csr1000v", params={"iface": "GigabitEthernet3", "address": "172.16.100.1", "netmask": "255.255.255.0"})↵
... ↵
↵

csr1000v#

csr1000v#terminal length 0
terminal length 0
csr1000v#terminal width 511
terminal width 511
csr1000v#

csr1000v#

csr1000v#

csr1000v#config term
config term
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v(config)#

csr1000v(config)#

csr1000v(config)#

csr1000v(config)#interface GigabitEthernet3
interface GigabitEthernet3
csr1000v(config-if)#

csr1000v(config-if)#ip address 172.16.100.1 255.255.255.0
ip address 172.16.100.1 255.255.255.0
csr1000v(config-if)#

csr1000v(config-if)#no shutdown
no shutdown
csr1000v(config-if)#

csr1000v(config-if)#do show ip interface brief | inc GigabitEthernet3
do show ip interface brief | inc GigabitEthernet3
GigabitEthernet3       172.16.100.1    YES manual up                    up      
csr1000v(config-if)#

csr1000v(config-if)#end
end
csr1000v#

csr1000v#
>>> print(r.bindings.yaml_format)↵
... ↵
↵
$alias: csr1000v
$host: 192.168.2.200
$password: s10nes
$port: '22'
$secret: s10nes
$username: hash
address: 172.16.100.1
iface: GigabitEthernet3
netmask: 255.255.255.0
status: up
>>>


  • API呼び出しによる動作確認
>>> r = await callout(path="/config?alias=csr1000v", method=PATCH, body={"iface": "GigabitEthernet3", "address": "172.16.100.1", "netmask": "255.255.255.0"})↵
... ↵
Connecting...
Done.
csr1000v#
config term
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v(config)#
interface GigabitEthernet3
csr1000v(config-if)#
ip address 172.16.100.1 255.255.255.0
csr1000v(config-if)#
no shutdown
csr1000v(config-if)#
do show ip interface brief | inc GigabitEthernet3
GigabitEthernet3       172.16.100.1    YES manual up                    up      
csr1000v(config-if)#

Qmonus> status = 'up'
↵
disable
csr1000v>

Disconnecting...
Done.
>>> print(MU(json.loads(r.body)).yaml_format)↵
... ↵
↵
csr1000v:
  code: 200
  output:
    bindings:
      $alias: csr1000v
      $host: 192.168.2.200
      $password: s10nes
      $port: '22'
      $secret: s10nes
      $username: hash
      address: 172.16.100.1
      iface: GigabitEthernet3
      netmask: 255.255.255.0
      status: up
    message: null
    responses:
    - |-
      interface GigabitEthernet3
      csr1000v(config-if)#
    - |-
      ip address 172.16.100.1 255.255.255.0
      csr1000v(config-if)#
    - |-
      no shutdown
      csr1000v(config-if)#
    - "do show ip interface brief | inc GigabitEthernet3\nGigabitEthernet3       172.16.100.1\
      \    YES manual up                    up      \ncsr1000v(config-if)#"
    status: Complete
>>>