てくなべ (tekunabe)

ansible / network automation / 学習メモ

RED HAT ANSIBLE NETWORK AUTOMATION のアップデート情報(翻訳)



この翻訳記事は Ansible 公式ブログである THE INSIDE PLAYBOOK の記事、RED HAT ANSIBLE NETWORK AUTOMATION UPDATESを、筆者であるSean Cavanaughさん及びRed Hat の許可を得て翻訳したものです。



f:id:akira6592:20181017134114p:plain 先日、今まで最大の AnsibleFest は成功を収めました。Ansible Engine 2.6、Ansible Tower 3.3、そして直近の Ansible Engine 2.7 のリリースで Red Hat の エンジニアリングチームが行った大きな機能拡張について、ネットワーク自動化の観点で振り返ってみたいと思います。すべての Ansible を愛するすべての人のためのリマインダーとして、アップグレードをなるべく簡単に行うためのポーティングガイドがあります!

このブログ記事では、以下の内容を説明します。

  • HTTPAPI コネクションプラグイン
    • Arista eAPI と Cisco NX-API のサポート
  • 新しいネットワーク自動化モジュール
    • net_get と net_put
    • netconf_get、netconf_rpc、netconf_config
    • cli_command と cli_config
  • 改善された Ansible Tower のユーザーエクスペリエンス
  • ネットワーク機器のための認証情報の管理
  • Ansible Tower のための カスタム Ansible 環境のサポート


HTTPAPI コネクションプラグイン

コネクションプラグインは、Ansible がターゲットホストに接続して、タスクを実行できるようにします。 Ansible 2.5 のリリースでは、netwrok_cli コネクションプラグインが導入されました。これにより、provider パラメーターが不要になり、ネットワークモジュールの標準化によって Linux ホストと同じように Playbook を作成し、操作できるようになりました。また、Red Hat Ansible Tower では、ネットワーク機器でも他の機器と同じように「machine credentials」を扱えるようになりました。もう「network credentials」は不要です。以前のブログ記事でも説明されていましたが、Linuxサーバー、Arista EOS スイッチ、Cisco ルーターでも、同じようにユーザー名やパスワードなどの認証情報を使用、保存できます。

しかし、eAPI や NX-API 経由の接続方法は、これまで従来の provider 方式の接続方法しかサポートされていませんでした。Ansible 2.6 ではこの制限がなくなり、代わりにトップレベル(訳注: conneciton: httpapiansible_connection: httpapi のような指定) の httapi 接続が利用できるようになりました。 どのようになるか見てみましょう。

まず、httpapi 接続を利用するために、ネットワーク機器側で eAPI や NX-API を有効にする必要があります。 幸い、これは Asnible でとても簡単にできてしまいます! ad-hoc コマンドは素早く Arosta EOS スイッチの eAPI を有効にできます。

[user@rhel7]$ ansible -m eos_eapi -c network_cli leaf01
leaf01 | SUCCESS => { 
   "ansible_facts": { 
     "eos_eapi_urls": { 
       "Ethernet1": [ 
            "https://192.168.0.14:443" 
        ], 
        "Management1": [ 
             "https://10.0.2.15:443" 
        ] 
     } 
    }, 
    "changed": false, 
    "commands": []
}

Arista EOS スイッチに実際に接続すると、show management api http-commands コマンドで API が有効になっていることがわかります。

leaf01# show management api http-commands
Enabled:      Yes
HTTPS server: running, set to use port 443
<<< ...略...>>>

以下の Playbook は単純な show version コマンドを実行し、デバッグ文ではコマンドタスクから与えられた JSON 出力の中からバージョンだけを返します。

---
- hosts: leaf01 
  connection: httpapi 
  gather_facts: false 
  tasks: 
    - name: type a simple arista command 
      eos_command: 
      commands: 
        - show version | json 
      register: command_output 

    - name: print command output to terminal window 
      debug: 
        var: command_output.stdout[0]["version"]

Playbook を実行すると、以下のような結果になります。

[user@rhel7]$ ansible-playbook playbook.yml
PLAY [leaf01]********************************************************

TASK [type a simple arista command] *********************************
ok: [leaf01]

TASK [print command output to terminal window] **********************
ok: [leaf01] => { 
     "command_output.stdout[0][\"version\"]": "4.20.1F" 

} 

PLAY RECAP***********************************************************
leaf01 : ok=2 changed=0 unreachable=0 failed=0

補足ですが、短縮コマンド(例:「show version」に対する「show ver」)は Arista eAPI では実行できません。完全なコマンドを使用する必要があります。httapi コネクションプラグインの詳細については関連ドキュメントを参照してください。


新しいネットワーク自動化モジュール

Ansible 2.6 と今後リリースされる 2.7(訳注: 2018/10/4リリース済み)には、7 つの新しいモジュールがあります。

net_get と net_put

  • net_get - ネットワーク機器から Ansible コントローラーにファイルをコピーする
  • net_put - Ansible コントローラーからネットワーク機器にファイルをコピーする
  • netconf_get - NETCONF が有効なネットワーク機器から設定や状態を取得する
  • netconf_rpc - NETCONF が有効なネットワーク機器上で操作を実行する
  • netconf_config - NETCONF が有効なネットワーク機器に設定XMLファイルを送信して設定し、変更を検出する。
  • cli_command - CLI ベースのネットワーク機器上で CLI コマンドを実行する
  • cli_config - テキストベースのコンフィグを network_cli 経由で ネットワーク機器に送る

net_getnet_put モジュールは、標準的な SCP や SFTP 転送プロトコルprotocol パラメーターで指定)を使用して、ネットワーク機器との間でファイルをコピーする、ベンダー非依存型のモジュールです。 どちらのモジュールも、 network_cli 接続方法を使用する必要があります。また、Ansible コントローラー上で scp がインストール(pip install scp)され、ネットワーク機器上で SCP(または SFTP)が有効になっている必要があります。

この Playbook を動作させるために、 あらかじめ Leaf01 で以下のことを実行したと仮定します。

leaf01#copy running-config flash:running_cfg_eos1.txt
Copy completed successfully.

ここでは、2つのタスクからなる Playbook を見ていきます。最初のタスクは、 Leaf01 からファイルをコピーし、2つ目のタスクは Leaf01 へファイルをコピーします。

---
- hosts: leaf01 
  connection: network_cli 
  gather_facts: false 
  tasks: 
   
   - name: COPY FILE FROM THE NETWORK DEVICE TO ANSIBLE CONTROLLER 
     net_get: 
       src: running_cfg_eos1.txt 

  - name: COPY FILE FROM THE ANSIBLE CONTROLLER TO THE NETWORK DEVICE 
    net_put: 
      src: temp.txt

netconf_get、netconf_rpc、netconf_config

  • netconf_get - NETCONF が有効なネットワーク機器から設定や状態を取得する
  • netconf_rpc - NETCONF が有効なネットワーク機器上で操作を実行する
  • netconf_config - NETCONF が有効なネットワーク機器に設定XMLファイルを送信して設定し、変更を検出する。

Network Configuration Protocol (NETCONF) は、IETF によって開発、標準化されたネットワーク管理プロトコルです。RFC 6241 として定義されているように、NETCONF はネットワーク機器の設定をインストール、操作、削除を行うことができます。NETCONF は SSH 接続によるコマンドライン (netowrk_cli)や、Cisco NX-API と Arista eAPI (httpapi)のような機器の API の代わりになるものです。

新しい netconf モジュールを紹介するために、まず junos_netconf モジュールを利用して、Juniper ルーターの netconf を有効にします。すべてのネットワーク機器が netconf をサポートするわけではありませんので、対象のプラットフォームをサポートしているベンダーのドキュメントを確認してください。

[user@rhel7 ~]$ ansible -m junos_netconf juniper -c network_cli
rtr4 | CHANGED => { 
    "changed": true, 
    "commands": [ 
         "set system services netconf ssh port 830" 
    ]
}
rtr3 | CHANGED => { 
    "changed": true, 
    "commands": [ 
         "set system services netconf ssh port 830" 
    ]
}

Juniper Networks は Operational Tags や、Configuration Tags を確認するために Junos XML API Explorer を提供しています。Juniper Network のドキュメントに例示されている、特定のインターフェースへの RPC リクエストの操作例を見てみましょう。

<rpc>
  <get-interface-information>
   <interface-name>ge-2/3/0</interface-name>
    <detail/>
 </get-interface-information>
</rpc>
]]>]]>

これは、とてもきれいに Playbook に変換できます。get-interface-informatio は RPC 呼び出しで、content パラメーターの配下には、キーと値のペアからなる追加パラメーターを定義します。この場合、interface-name という1つのオプションがあり、インターフェース em1.0 だけを見ます。debug モジュールを利用してターミナル上に結果を出力するために、register というタスクレベルのパラメーターを使用して結果を保存します。netconf_rpc モジュールは、XML の戻り値を直接 JSON に変換することもできます。

---
- name: RUN A NETCONF COMMAND 
  hosts: juniper 
  gather_facts: no 
  connection: netconf 

  tasks: 

    - name: GET INTERFACE INFO 
      netconf_rpc: 
        display: json 
        rpc: get-interface-information 
        content: 
          interface-name: "em1.0" 
      register: netconf_info 

    - name: PRINT OUTPUT TO TERMINAL WINDOW 
      debug: 
        var: netconf_info

Playbook を実行すると、以下のように返されます。

---
- name: RUN A NETCONF COMMAND 
  hosts: juniper 
  gather_facts: no 
  connection: netconf 

  tasks: 

    - name: GET INTERFACE INFO 
      netconf_rpc: 
        display: json 
        rpc: get-interface-information 
        content: 
          interface-name: "em1.0" 
      register: netconf_info 

    - name: PRINT OUTPUT TO TERMINAL WINDOW 
      debug: 
        var: netconf_info
<<< ...略...>>>

Juniper プラットフォームについての詳細は、 Juniper Platform Guide を参照してください。 netconf コネクションプラグインについての詳細は、 NETCONF documentation を参照してください。

cli_command と cli_config

  • cli_command - CLI ベースのネットワーク機器上で CLI コマンドを実行する
  • cli_config - テキストベースのコンフィグを network_cli 経由で ネットワーク機器に送る

Aniable Engine 2.7 リリースで利用可能な cli_commandcli_config モジュールは、ネットワークプラットフォームを自動化するベンダー非依存型のモジュールです。これらのモジュールは、適切な cliconf プラグインを使用するために、 ansible_network_os 変数(インベントリファイルや group_vars ディレクトリ内で定義)を使用します。これにより、ネットワーク自動化のプレイブックのベンダー中立性が向上します。すべての ansible_network_os の値のリストは、ドキュメントを参照してください。このリリースではプラットフォーム固有のモジュールは廃止されないため、既存の Playbook を更新するために急ぐことはありません!詳細については、公式のポーティングガイドを参照してください。


f:id:akira6592:20181017134617p:plain

Playbook がどのようになるか見てみましょう。 この Playbook は IOS-XE が動作している Cisco Cloud Services Routers (CSR) に対して実行します。 ansible_network_os 変数は、インベントリーファイルの中で ios に設定します。

<インベントリーファイル抜粋>
[cisco]
rtr1 ansible_host=34.203.197.120 
rtr2 ansible_host=34.224.60.230

[cisco:vars]
ansible_ssh_user=ec2-user
ansible_network_os=ios

こちらが cli_configcli_command モジュールを利用した Playbook です。

---
- name: AGNOSTIC PLAYBOOK 
  hosts: cisco 
  gather_facts: no 
  connection: network_cli 
  
  tasks: 
     - name: CONFIGURE DNS 
       cli_config: 
         config: ip name-server 8.8.8.8 

     - name: CHECK CONFIGURATION 
         cli_command: 
           command: show run | i ip name-server 
       register: cisco_output 

     - name: PRINT OUTPUT TO SCREEN 
       debug: 
         var: cisco_output.stdout

最後に、Playbook の実行結果はこのようになります。

[user@rhel7 ~]$ ansible-playbook cli.yml

PLAY [AGNOSTIC PLAYBOOK] *********************************************

TASK [CONFIGURE DNS] *************************************************
ok: [rtr1]
ok: [rtr2]

TASK [CHECK CONFIGURATION] *******************************************
ok: [rtr1]
ok: [rtr2]

TASK [PRINT OUTPUT TO SCREEN] ****************************************
ok: [rtr1] => { 
    "cisco_output.stdout": "ip name-server 8.8.8.8"
}
ok: 
    [rtr2] => { 
    "cisco_output.stdout": "ip name-server 8.8.8.8"
}

PLAY RECAP **********************************************************
rtr1 : ok=3 changed=0 unreachable=0 failed=0
rtr2 : ok=3 changed=0 unreachable=0 failed=0

上記の出力を見ると、適切なネットワーク機器に対応するコマンド構文を使用している限り、モジュールが冪統( idempotent/idempotency) であることにお気づきでしょう。


改善された Ansible Tower のユーザーエクスペリエンス

Red Hat Ansible Tower 3.3 のリリースでは、Web ユーザー・インタフェースがより機能的に改善され、少ないクリック数で多くの作業を完了できるようになりました。アップグレードされた Ansible Tower 3.3 にログインすると、刷新された UXが現れます。


f:id:akira6592:20181017134725p:plain

認証情報の管理、ジョブスケジューリング、インベントリースクリプト、ロールベースのアクセス制御 (RBAC) 、通知などは、左メニューからワンクリックで行えるようになりました。ジョブビューでは、開始時間、ジョブを開始したユーザー、ジョブが実行されたインベントリー、Playbook が取得されたプロジェクトなどの詳細情報が最上位に表示されます。


f:id:akira6592:20181017134753p:plain

ネットワーク機器のための認証情報の管理

Red Hat Ansible Tower 3.3 では、ネットワーク機器のための認証情報の管理が以前より簡単になりました。 新しい Web UIでは、 Credentials メニューを1クリックするだけでアクセスできます。左メニューの Resources の下の Credentials を選択してください。

f:id:akira6592:20181017134818p:plain

以前までは Network と呼ばれる認証情報タイプがありました。これは、従来の provider 方式で利用する環境変数 ANSIBLE_NET_USERNAMEANSIBLE_NET_USERNAME を設定するものです。Ansible Tower の Credentials の章に記載されているとおり、既存の Playbook のためにまだ完全にサポートされています。しかし、新しい httpapinetwork_cli 接続方法ではNetwork という認証情報タイプはもう必要ありません。ユーザー名とパスワードは、 Ansible が標準の Linux ホストに接続するのと同じように利用されます。そのため、Machine 認証情報タイプはネットワーク機器にも利用できるようになりました。Machine 認証情報 を選択し、ユーザー名とパスワード(または SSH 鍵)を入力するだけです。


f:id:akira6592:20181017134839p:plain

注意: 認証情報に一度保存すると、Ansible Tower はパスワードを暗号化します。

f:id:akira6592:20181017134913p:plain

暗号化によって、平文パスワードを見られることなく、個人またはグループに認証情報を利用してもらうことができます。認証情報の仕組みや、使用されている暗号化、その他の認証情報タイプ(Amazon AWSMicrosoft Azure、Google GCEなど)についての詳細は、関連のAnsible Tower のドキュメントを参照してください。

新しい Red Hat Ansible Tower 3.3 についての詳細な説明についてはChris Short によるこちらのブログ記事を参照してください。


Ansible Tower のためのカスタム Ansible 環境のサポート

もし、いくつかの Playbook は Ansible Engine 2.4.2 で、また別の Playbook はAnsible Engine 2.6.4 で実行したい場合は、どうしたらよいでしょうか?このようなユースケースのために、 Ansible Tower は virtualenv を利用します。Virtualenv は、依存関係やバージョンの違いよる問題を避けるために、隔離された Python環境を作るツールです。Ansible Tower 3.3 のリリースでは、Organization、Project、Job Template のレベルで、virtualenv を設定できるようになりました。Ansible Tower でネットワーク機器のバックアップを行う Job Template を見てみましょう。


f:id:akira6592:20181017135008p:plain

Ansible Tower に複数の virtualenv による仮想環境が設定されると、Web UI に Ansible Environment のドロップダウンメニューが表示されます。これにより、特定のジョブをどのバージョンの Ansible で実行するかを簡単に選べるようになります。

f:id:akira6592:20181017135037p:plain

もし、provider 方式による Playbook(Ansible 2.4 以前)と、httpapinetwork_cli といったコネクションプラグインによる新しい Playbook(Ansible 2.5 以降)が混合しているネットワーク自動化の Playbook 群がある場合、各ジョブに異なるバージョンの Ansible を簡単に割り当てることができます。また、他のユースケースとしては、開発環境とプロダクション環境との間で Ansible のバージョンが異なる場合が考えられます。 別の考え方をすると、Ansible Tower をアップグレードしても、利用できる Ansible Engine のバージョンが1つに固定されるわけではない、ということです。 これにより、さまざまな種類のネットワーク機器、環境、ユースケースを自動化するための柔軟性がより向上します。

Ansible Tower で virtualenv を使用する方法の詳細については 関連ドキュメントを参照してください。


お礼

Ansible Networking チームは、今後リリース予定(訳注: 2018/10/04リリース 済み)の Ansible 2.7 に興奮しています。また、これを支えてきた全てのネットワーキングパートナーとコミュニティメンバーに温かい感謝の気持ち伝えたいです。 あなたのフィードバックやご意見、アイディアをお待ちしています。そして、Ansible networking community への参加を歓迎します。




翻訳者コメント

f:id:akira6592:20181017135052p:plain:w60

Ansible は、2016年リリースの Ansible 2.1 でネットワーク機器に標準対応しました。最近は、単にモジュールや対応プラットフォームの追加だけでなく、ベンダー非依存型のモジュールの登場や、ネットワークモジュール固有の Playbook の書き方(provider オプションによる接続情報の指定)の排除など、全体的な機能の底上げが図られてきているように感じています。

本記事では、このような最近の動向がまとめられていて有益な記事だと感じ、日本語でも共有したく翻訳を申し出しました。許可をいただきありがとうございました。

Mr. Sean Cavanaugh, Thank you for a wonderful blog post.

私も独自に Ansible 2.7 でのネットワークモジュールのアップデート情報をまとめた記事がありますので、あわせてご参照ください。 tekunabe.hatenablog.jp

なお、Red Hat Ansible Engine の参考価格や総合的な日本語情報はこちらです。 www.redhat.com


[nornir] Python 製自動化フレームワーク「nornir」かんたんチュートリアル(Ansibleと比較しながら)


※2018/12/18 更新: nornir 2.0.0 リリース に伴い、全面的に 2.0.0 対応に更新しました。

■ nornir とは

nornir は、Python 製の新しい自動化フレームワークです。 netmiko や NAPALM も取り込んでいるため、ネットワーク機器にも対応しています。 この記事では、ネットワーク機器を対象としたチュートリアルをご紹介します。

チュートリアルは、2018/12/18 現在 の最新バージョンである [2.0.0] を対象にしています。 nornir 1.x 系と 2.x 系ではコネクションパラメータ名の方法が異なりますので、本記事の最後記事の最後で補足します。

github.com

https://nornir.readthedocs.io/en/stable/index.html

  • 環境
    • nornir 2.0.0
    • Python 3.6.5
    • Ansible 2.7.5 (比較用)
    • Centos 7.5.1804

Ansible との比較

Ansible や Salt は構成の定義を YAMLDSLという見方もできると思います)で記述します。 一方 nornir は純粋に Python のライブラリなので、実現したいことを Python でコーディングします。 Ansible で Playbook を書いていて、だんだん複雑になり「コードで書きたいな・・」と思ったときに、代替手段になるかもしれません。

netmiko や NAPALM との比較

ネットワーク機器への自動化といえば、netmikoNAPALMといった、Python ライブラリもあります。 nornir は これらのライブラリの機能に、インベントリや変数管理の仕組みをつけたフレームワークのようなものです。

requirements.txt を確認すると分かるように、nornir をインストールすると、netmiko や NAPALM もインストールされます。


■ インストールは pip install nornir

nornir のインストール手順は以下の通りです。

pip install nornir

必要に応じて virtualenv などを利用して、環境を分けてください。

なお、Python 2.7 もまだサポートされていますが、推奨は 3.6 とされています。


チュートリアル: Junos の 2台 の show version 結果を表示

インストールができたところで、Juniper Junos の 2台の show version コマンド結果を表示するチュートリアルをはじめます。実際にネットワーク機器に接続する部には NAPALM を使用します。

説明のために、同じことを Ansible(2.7.5) で実現する場合と比較しながら説明します。

インベントリと変数定義の準備

対象ホストを定義するインベントリと、それらに適用される変数定義のファイルを準備します。

nornir の場合

hosts.yaml(今回のコードの場合、ファイル名は固定)で、ホストの名前、所属させるグループ、実際に接続する IP アドレスを定義します。

vsrx1:                   # (nornir管理上の)ホスト名
  groups:                # 所属するグループの定義
    - junos              # グループ名
  hostname: 172.16.0.1   # (実際に接続するための)ホスト名または IP アドレス
vsrx2:
  groups:
    - junos
  hostname: 172.16.0.2

続いて、groups.yaml(今回のコードの場合、ファイル名は固定)で、グループの名前、と利用する変数(ログイン情報など)を定義します。

junos:                    # グループ名
  username: root          # ログインユーザー名      
  password: pasword9999   # ログインパスワード 
  platform: junos         # プラットフォーム名  

hosts.yaml 内の groups で指定した junos が、groups.yml 内の junos: に対応します。 プラットフォーム名の junos は、対象のネットワーク機器によって変更します。もし Cisco IOS であれば ios を指定します。

補足: 対象に指定できるプラットフォームについて

指定できるプラットフォームの一覧は nornir のドキュメントには見当たりませんでした。 実際のネットワーク機器への接続部分に、NAPALM 利用するか netmiko を利用するかによって異なります。

NAPALM を利用する場合(本チュートリアルはこちら)

NAPALM ドキュメントのGeneral support matrixDriver Name に記載があるもの。

NAPALM 2.3.3 現在は以下(クリックで表示)

  • eos
  • junos
  • iosxr
  • nxos
  • nxos_ssh
  • ios
netmiko を利用する場合

netmiko/ssh_dispatcher.pyCLASS_MAPPER で定義されるもの。

netmiko 2.3.0 現在は以下( 末尾に _ssh を付けた形も可)(クリックで表示)

  • a10
  • accedian
  • alcatel_aos
  • alcatel_sros
  • apresia_aeos
  • arista_eos
  • aruba_os
  • avaya_ers
  • avaya_vsp
  • brocade_fastiron
  • brocade_netiron
  • brocade_nos
  • brocade_vdx
  • brocade_vyos
  • calix_b6
  • checkpoint_gaia
  • ciena_saos
  • cisco_asa
  • cisco_ios
  • cisco_nxos
  • cisco_s300
  • cisco_tp
  • cisco_wlc
  • cisco_xe
  • cisco_xr
  • coriant
  • dell_dnos9
  • dell_force10
  • dell_isilon
  • dell_os10
  • dell_os6
  • dell_os9
  • dell_powerconnect
  • eltex
  • enterasys
  • extreme
  • extreme_ers
  • extreme_exos
  • extreme_netiron
  • extreme_nos
  • extreme_slx
  • extreme_vdx
  • extreme_vsp
  • extreme_wing
  • f5_linux
  • f5_ltm
  • f5_tmsh
  • fortinet
  • generic_termserver
  • hp_comware
  • hp_procurve
  • huawei
  • huawei_vrpv8
  • ipinfusion_ocnos
  • juniper
  • juniper_junos
  • linux
  • mellanox
  • mrv_optiswitch
  • netapp_cdot
  • netscaler
  • ovs_linux
  • paloalto_panos
  • pluribus
  • quanta_mesh
  • rad_etx
  • ruckus_fastiron
  • ubiquiti_edge
  • ubiquiti_edgeswitch
  • vyatta_vyos
  • vyos

なお、私が試した限り netmiko にないプラットフォーム名 ios でも netmiko を利用できました。plugins/connections/netmiko.py にある以下の変換表によって変換されて利用できたようです。

napalm_to_netmiko_map = {
    "ios": "cisco_ios",
    "nxos": "cisco_nxos",
    "eos": "arista_eos",
    "junos": "juniper_junos",
    "iosxr": "cisco_xr",
}

Ansible の場合

Ansible の場合は、以下のようなインベントリファイルと、グループ変数定義ファイルを用意します。

  • inventory.ini
[junos]
vsrx1 ansible_host=172.16.0.1
vsrx2 ansible_host=172.16.0.2
  • group_vars/junos.yaml
---
ansible_network_os: junos
ansible_connection: netconf   # 今回は network_cli でも可
ansible_user: root
ansible_password: pasword9999

実行ファイルの準備

nornir の場合

hosts.yamlgroups.yaml と同じディレクトに、以下の Python スクリプトを作成します。

  • show_version.py
from nornir import InitNornir
from nornir.plugins.functions.text import print_result
from nornir.plugins.tasks.networking import napalm_cli

# オブジェクトの初期化
nr = InitNornir()

# 実行
results = nr.run(
    # コマンドを直接実行するタスク napalm_cli を呼び出して、コマンドは show version を指定
    task=napalm_cli, commands=["show version"]  
)

# nornir 用の出力関数で結果を出力
print_result(results)

今回は、何も指定していませんが、InitNorir で初期化時に、パラメータを渡すことによって、ホストファイル名の指定などの設定ができます。詳細は API ドキュメントを参照してください。

補足: norinir + netmiko を利用する場合

前述の例では、実際のネットワーク機器への接続部分に NAPALM を使用しましたが、同様の処理は netmiko でもできます。 netmiko を利用する場合のサンプルは以下の通りです。

  • show_version_netmiko.py
from nornir import InitNornir
from nornir.plugins.functions.text import print_result
from nornir.plugins.tasks.networking import netmiko_send_command  # netmiko 

# オブジェクトの初期化
nr = InitNornir()

# 実行
results = nr.run(
    # コマンドを直接実行するタスク netmiko_send_command を呼び出して、コマンドは show version を指定
    task=netmiko_send_command,
    command_string="show version"  # リストではなく文字列を指定
)

# nornir 用の出力関数で結果を出力
print_result(results)

Ansible の場合

以下の Playbook を作成します。

  • show_version.yml
- hosts: all
  gather_facts: no

  tasks:
    - name: show version
      junos_command:
        commands:
          - show version
      register: result

    - name: debug version
      debug:
        msg: "{{ result.stdout_lines[0] }}"

実行

nornir の場合

python コマンドで、先ほど作成したスクリプトを実行します。

$ python test.py
napalm_cli**********************************************************************
* vsrx1 ** changed : False *****************************************************
vvvv napalm_cli ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
{ u'show version': u'\nHostname: vsrx1\nModel: firefly-perimeter\nJUNOS Software Release [12.1X47-D15.4]\n'}
^^^^ END napalm_cli ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* vsrx2 ** changed : False *****************************************************
vvvv napalm_cli ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
{ u'show version': u'\nHostname: vsrx2\nModel: firefly-perimeter\nJUNOS Software Release [12.1X47-D15.4]\n'}
^^^^ END napalm_cli ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

nornir を利用したコードで、無事に show version コマンドの結果が表示されました。

以下のような色の表示になります。

f:id:akira6592:20181218171119p:plain
nornir による show version 実行結果

Ansible の場合

$ ansible-playbook -i inventory.ini show_versoin.yml

PLAY [all] *******************************************************************

TASK [show version] **********************************************************
ok: [vsrx1]
ok: [vsrx2]

TASK [debug version] *********************************************************
ok: [vsrx1] => {
    "msg": [
        "Hostname: vsrx1",
        "Model: firefly-perimeter",
        "JUNOS Software Release [12.1X47-D15.4]"
    ]
}
ok: [vsrx2] => {
    "msg": [
        "Hostname: vsrx2",
        "Model: firefly-perimeter",
        "JUNOS Software Release [12.1X47-D15.4]"
    ]
}

PLAY RECAP *******************************************************************
vsrx1                      : ok=2    changed=0    unreachable=0    failed=0
vsrx2                      : ok=2    changed=0    unreachable=0    failed=0

こちらも無事に show version コマンドの結果が表示されました。


■ まとめ

今回は、簡単な show コマンド実行するだけのサンプルをご紹介しました。 他にも、設定系コマンドや、show コマンドの結果をパースして表示する機能などもあります。インベントリについても、今回は定義された全ホストに接続しましたが、フィルターする機能もあります。

「コードで処理を書きたい。でも Ansible のようなインベントリや変数管理の仕組みは欲しい。」という場合に、nornir が候補になるかもしれません。


■ 参考


Nornir - Python automation framework [Part 16] Network Programmability Stream

補足: nornir 1.x と 2.x の違い

nornir 1.x 系と 2.x 系(2.0.0 は 2018/12 リリース)では、コネクションパラメータ名などが異なります。

コネクションパラメータ名

1.x 2.x 補足
nornir_host hostname
nornir_nos platform
nornir_ssh_port port チュートリアル未使用
nornir_network_api_port port チュートリアル未使用
nornir_username username
nornir_password password

InitNornir のインポート方法

  • 1.x
from nornir.core import InitNornir
  • 2.x
from nornir import InitNornir

nornir 1.x 系と 2.x 系へのアップグレード方法の詳細は以下のページを参照してください。 https://nornir.readthedocs.io/en/latest/upgrading/1_to_2.html

「インストラクショナルデザイン―教師のためのルールブック」個人的まとめ

教育資料や発表資料を、分かりやすく、効果的なものにするヒントになるかと思い「インストラクショナルデザイン―教師のためのルールブック」という本を読みました。

インストラクショナルデザイン―教師のためのルールブック

インストラクショナルデザイン―教師のためのルールブック

タイトルに「教師のための」とあるように、確かに読み手が教師であることを前提としている本ですが、そうでなくても、何かを伝える役割の人とにとっては得るものはあると思いました。

簡単ですが、本書で個人的にポイントだと思ったことをまとめます。

  • インストラクションとは、何らかの行動を引き出すための仕掛け。それにはデザインが必要。
  • 知能、技能、遂行に分けて考える
  • 分かりやすく教えるための3ステップ
    • (1) 説明する / 見せる
    • (2) 行動させる /練習させる
    • (3) 習得を確認させる
      • フィードバックすることで、自分で行動を評価できるようにする
      • あとどのくらいが学ぶべきことが残っているかも判斷でき、動機づけになる
  • 正答率が80%くらいの問題を出し、100%になったら、少しレベルを上げて標的行動に近づけていく
    • スモールステップ
    • 「強化」の機会を増やせる
  • 自分の行動が変化していくことを学び手に分かるようにフィードバックしていくデザインが有効
    • 楽しさ
  • 分かったかどうか質問するのではなく、概念の例と例外を区別するような質問をする

ネットワーク自動化 Python ライブラリ NAPALM 2.3.3 リリース、公式 Docker イメージ追加など

■ NAPALM 2.3.3 リリース

github.com

2018/10/15 にネットワーク自動化 Python ライブラリである NAPALM 2.3.3がリリースされました。詳細は 2.3.3 CHNGELOG に記載があります。

この記事では、個人的に気になった Docker イメージの追加と、モックの利用法のドキュメント追加についてピックアップします。


■Docker イメージの追加

Dockerhub の napalmautomation/napalmに 「Official NAPALM Docker image」としてあがっています。 Dockerfile はこちらで、Debian ベースのようです。

さっそく、Junos を利用して簡単な動作確認をしてみます。

docker run/bin/bash を起動

$ sudo docker run -i -t napalmautomation/napalm /bin/bash
Unable to find image 'napalmautomation/napalm:latest' locallyTrying to pull repository docker.io/napalmautomation/napalm ...
latest: Pulling from docker.io/napalmautomation/napalm05d1a5232b46: Pull completed55900eb92a3: Pull completea30024124f3c: Pull complete
8743e06e9fcd: Pull completeDigest: sha256:ddf01b2ee4d2af5edbc3be4906e1f059410b8b776b519fe08d3230cad0ca7a0a] 37.02Status: Downloaded newer image for docker.io/napalmautomation/napalm:latest
root@58839899c7fd:/#  

python インタプリターを起動して、対象機器にログインセッションをオープン

root@58839899c7fd:/# python

>>> from pprint import pprint  # ディクショナリを見やすくするため
>>> from napalm import get_network_driver
>>> driver = get_network_driver('junos')
>>> device = driver('172.16.0.1', 'testuser', 'testpass')
>>> device.open()

デフォルトルートの情報を表示

>>> pprint(device.get_route_to('0.0.0.0'))
{u'0.0.0.0/0': [{'age': 507,
                 'current_active': True,
                 'inactive_reason': u'',
                 'last_active': True,
                 'next_hop': u'10.0.2.2',
                 'outgoing_interface': u'ge-0/0/0.0',
                 'preference': 12,
                 'protocol': u'Access-internal',
                 u'protocol_attributes': {},
                 'routing_table': u'inet.0',
                 'selected_next_hop': True}]}

show version コマンドの結果を表示

>>> pprint(device.cli(['show version']))
{u'show version': u'\nHostname: vsrx1\nModel: firefly-perimeter\nJUNOS Software Release [12.1X47-D15.4]\n'}

セッションをクローズ

>>> device.close()


docker run コマンド経由で napalm コマンドを実行する

今度は、 bash を起動せずに、docker run 経由で napalm というコマンドラインツールを使ってデフォルトルートの情報を表示します。 (ホスト側に NAPALM はインストール不要)

$ sudo docker run -i -t napalmautomation/napalm napalm --user testuser--password testpass--vendor junos 172.16.0.1 call get_route_to --method-kwargs "destination='0.0.0.0'"
{
    "0.0.0.0/0": [
        {
            "protocol": "Access-internal",
            "last_active": true,
            "outgoing_interface": "ge-0/0/0.0",
            "current_active": true,
            "routing_table": "inet.0",
            "next_hop": "10.0.2.2",
            "selected_next_hop": true,
            "preference": 12,
            "inactive_reason": "",
            "age": 2210,
            "protocol_attributes": {}
        }
    ]
}

簡単ですが動作確認できました。


■ Napalm mock driver を利用したチュートリアルの追加

単体テストをする際のモックの使い方に関するチュートリアルページが追加されました。 https://napalm.readthedocs.io/en/latest/tutorials/mock_driver.html

このモックを利用すると、ネットワーク機器を用意せずに単体テストができるようです。

通常であれば、iosjunos のようにネットワークOS名を指定する箇所に mock を指定して利用します。

import napalm
driver = napalm.get_network_driver('mock')


■ その他

他にも、バグフィックスなどがされているようですので、詳細は以下の CHENGELOG を参照してください。 github.com

#ssmjp 2018/10 で「Ansibleではじめるサーバー・ネットワークの自動化 (Ansible2.7情報つき)」という発表をしました

■ Ansibleではじめるサーバー・ネットワークの自動化 (Ansible2.7情報つき)

2018/10/12 開催の #ssmjp 2018/10 で「Ansibleではじめるサーバー・ネットワークの自動化 (Ansible2.7情報つき)」という発表をさせていだきました。

www.slideshare.net

別のイベントで発表した資料をベースにして、10/04 にリリースされた Ansible 2.7 のアップデート情報を追加しました。

https://image.slidesharecdn.com/201810ssmjpansible-181012101437/95/ansible-ansible2720181012-17-638.jpg?cb=1539422741

3 と 4 については、CHANGELOG にも掲載されていないので、別途ブログにもまとめておきました。

また、 Web サーバーのインストールとコンテンツのデプロイでは、Playbook を作成するところから、動作後の確認までをデモしました。 ちゃんと練習したおかげで、どうにか一発OKでした。


■ 全員 Ansible

今回の #ssmjp は「Ansible を語る回」なので、登壇者全員が Ansible についての発表でした。

AnsibleではじめるNW設定の自動化について - Cisco(VIRL)編 - (すがいさん)

www.slideshare.net

ネットワーク構成を変数で表現してなるべく同じPlaybookを再利用するのが興味深かったです。 あと、VIRL(自由度の高いネットワーク機器のシミュレーターソフト)欲しくなりました。 そして、資料内で私の記事をたくさん紹介していただきありがとうございました。残しておいてよかったです。

残念な現場にAnsibleを適応してみた過去と現在と未来 (@togakushiさん)

(資料の一般公開は無し)

大変苦労された貴重なお話を伺えました。


■ まとめ的リンク


■ さいごに

今回の登壇のお話は、実は4月に頂いていました。ssmjp は 基本的に毎月開催されている老舗イベントで予定がいっぱいという状態だったので、ようやく開催というかたちになりました。継続できるのは素晴らしいですね。 この度はこのような機会を頂きまことにありがとうございました。 参加していただいた皆まもありがとうございました!

ssmjp.connpass.com

ssm.pkan.org

【Ansible】データから「select * from users where name="hoge"」的な抽出ができる selectattr

SQL の where 句のように ディクショナリのリストから抽出するには selectattr を利用

以下のようなディクショナリのリストがあるとします。DBにおけるテーブルのデータのような構造です。

users:
  - name: yamada
    age: 42
  - name: tanaka
    age: 26
  - name: suzuki
    age: 32
  - name: sato
    age: 27

ここで、「 namesato であるディクショナリのリスト」を抽出したい場合は、 selectattr を利用して以下のようにフィルター(Jinja2の機能)します。

  • selectattr によるフィルタ
- debug:
    msg: "{{ users | selectattr('name', '==', 'sato') | list }}"
  • 結果
    "msg": [
        {
            "age": 27,
            "name": "sato"
        }
    ]

無地に1件が抽出されました。 SQL でいうと select * from users where name='sato' のようなイメージです。


■ 応用例: 部分一致、以上、以下など指定もできる

先ほど使用した == (または qeualtoeq)では完全一致でしたが、他にもあります。

!= または ne: 〜ではない

- debug:
    msg: "{{ users | selectattr('name', '!=', 'sato') | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 26,
            "name": "tanaka"
        },
        {
            "age": 32,
            "name": "suzuki"
        }
    ]

in: 〜に含まれる

- debug:
    msg: "{{ users | selectattr('name', 'in', 'sssssatoooo') | list }}"
    "msg": [
        {
            "age": 27,
            "name": "sato"
        }
    ]

> または gt: 〜より大きい

- debug:
    msg: "{{ users | selectattr('age', '>', 27) | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 32,
            "name": "suzuki"
        }
    ]

>= または ge: 〜以上

- debug:
    msg: "{{ users | selectattr('age', '>=', 27) | list }}"
    "msg": [
        {
            "age": 42,
            "name": "yamada"
        },
        {
            "age": 32,
            "name": "suzuki"
        },
        {
            "age": 27,
            "name": "sato"
        }
    ]

< または lt: 〜以下

- debug:
    msg: "{{ users | selectattr('age', '<', 27) | list }}"
    "msg": [
        {
            "age": 26,
            "name": "tanaka"
        }
    ]

<= または le: 〜以下

- debug:
    msg: "{{ users | selectattr('age', '<=', 27) | list }}"
    "msg": [
        {
            "age": 26,
            "name": "tanaka"
        },
        {
            "age": 27,
            "name": "sato"
        }
    ]


■ 補足: ループと when の組み合わせとの比較

selectattr のように抽出してから何かを処理する方法の他にも、全件ループと when によって処理条件を指定する方法があります。 条件に合わなかった分のタスクは skipping になります。

  • Playbook: ループと when の組み合わせる場合
- debug:
    msg: "{{ item }}"
  when:
    - item.age <= 27
  loop: "{{ users }}"
  • 結果
skipping: [localhost] => (item={u'age': 42, u'name': u'yamada'})
ok: [localhost] => (item={u'age': 26, u'name': u'tanaka'}) => {
    "msg": {
        "age": 26,
        "name": "tanaka"
    }
}
skipping: [localhost] => (item={u'age': 32, u'name': u'suzuki'})
ok: [localhost] => (item={u'age': 27, u'name': u'sato'}) => {
    "msg": {
        "age": 27,
        "name": "sato"
    }
}

先に selectattr でフィルタしたほうがログがスッキリして、処理速度も早いかもしれません。

Playbook としてのシンプルさという点は、ループと when の組み合わせのほうがよさそうです。

参考: エラーが発生する場合

TemplateRuntimeError: no test named 'equalto' のようなエラーが発生し場合は、 Jinja2 のバージョンを確認してください。利用のバージョンで、指定した評価式が対応していないことがあります。

stackoverflow.com

【Ansible】pip モジュールでバージョン指定しながら複数のパッケージを指定できるようになった(Ansible 2.7新機能)

■ Ansible 2.7 で pip モジュールの name オプションで、バージョン指定しながら複数のパッケージを指定できるように

Ansible の pip モジュールでは、これまで name オプションの値をリストで指定すると、pip install pkgA pkgB pkgC のように、複数のパッケージを扱うことができました。しかし、pkgA==1.0.0 のようなバージョン指定と、リストによる複数の指定は併用できませんでした。 Ansible 2.7 では、バージョン指定しながら複数のパッケージを指定できるようになりました。

Playbook

- name: pip test
  pip:
    name:
      - netmiko>=2.2.0
      - ncclient==0.6.0
      - requests

コマンドイメージ

pip install netmiko>=2.2.0 ncclient==0.6.0 requests

この機能については、CHANGELOGには掲載されていませんが、pip モジュールのドキュメントname のオプションの説明として、以下のように記載されています。

This can be a list (since 2.2) and contain version specifiers (since 2.7).

これに伴って、例も2つ追加されています。

# Install (bottle) python package with version specifiers
- pip:
    name: bottle>0.10,<0.20,!=0.11

# Install multi python packages with version specifiers
- pip:
    name:
      - django>1.11.0,<1.12.0
      - bottle>0.10,<0.20,!=0.11



■(補足)Ansible 2.6 までの場合

比較のために、Ansible 2.6 までの状況について説明します。(2.7でも引き続き利用できる方法です)

requirements で代替可能

必要なパッケージ名と(必要であれば)バージョンを定義したリストファイルを用意して、requirements オプションで指定することでも代替可能です。

Playbook

 - name: pip test
    pip:
      requirements: /vagrant/test/requirements.txt

requirements.txt

netmiko>=2.2.0
ncclient==0.6.0
requests

コマンドイメージ

 pip install -r requirements.txt

パッケージ名だけの複数指定はOK

バージョンの指定をしなければ、リストによるパッケージ名だけの複数指定はできます。

Playbook

- name: pip test
  pip:
    name:
      - netmiko
      - ncclient
      - request

コマンドイメージ

pip intall netmiko ncclient request

1つパッケージ名だけならバージョン指定OK

パッケージ名の指定が1つだけなら、バージョン指定ができます。

Playbook

- name: pip test
  pip:
    name: netmiko
    version: 2.2.0

コマンドイメージ

pip intall netmiko==2.0.0

ですが、バージョン決め打ちの指定しかできず、 version: <= 2.2.0 といった指定はできません。

ループで回せばバージョン指定しながら複数のパッケージを指定(制限あり)

今回 Ansible 2.7 でできるようになったことの近いことは、with_listloop によるループでもできます。ただし、以下のような制限があります。

  • 「1つパッケージ名だけならバージョン指定OK」で説明した事情と同じく、<<= などの不等号を含むバージョン指定はきない
  • バージョン指定をしない場合は | default(omit) のような工夫が必要
  • ループするのでやや遅い

Playbook

- name: pip test
  pip:
    name: "{{ item.name }}"
    version: "{{ item.version | default(omit) }}"
  loop:
    - { name: "netmiko", version: "2.2.2" }
    - { name: "ncclient", version: "0.6.0" }
    - { name: "requests" }

コマンドイメージ(pip コマンド自体が複数)

pip intall netmiko==2.2.0
pip install ncclient==0.6.0
pip install requests

おわりに

地味なアップデートですが、これがやりたかった!というからもいらっしゃるのではないでしょうか。 このように、Ansible のアップデートは、CHANGELOG にな掲載されないものもあります。 普段お使いのモジュールの説明ドキュメントを見ると、なにか新しい発見があるかもしれません。

モジュール一覧はこちら