てくなべ (tekunabe)

ansible / network / automation

[Ansible] 小ネタ: Playbook は JSON で書ける

はじめに

※あくまで小ネタの話なので実運用で利用するのはおすすめしません。

YAML は JSON のほぼスーパーセットなのであれば、JSON で Playbook を書けるのでは?と思ったことがあったので試してみました。

Playbook

$ cat test.yml 
[
  {
    "gather_facts": false, 
    "connection": "local", 
    "tasks": [
      {
        "debug": {
          "msg": "hello!"
        }
      }
    ], 
    "hosts": "localhost"
  }
]
  • (参考) 上記 Playbook を通常通り YAML で書いた場合
- hosts: localhost
  gather_facts: false
  connection: local
  
  tasks:
    - debug:
        msg: "hello!"

YAML から JSON への変換を試すには、このようなサービスを利用するとお手軽です。

実行

$ ansible-playbook -i localhost, test.yml 

PLAY [localhost] ******************************************************************************

TASK [debug] **********************************************************************************
ok: [localhost] => {
    "msg": "hello!"
}

PLAY RECAP ************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

できました。(Ansible 2.7)

Playbook のリストやディクショナリの関係がよく分からなくなったりした場合、JSONで書いたり眺めたりすると良い練習になるかも知れません。

補足

Ansible では YAML 1.1 を扱いますが、YAML 1.2 の仕様には

The primary objective of this revision is to bring YAML into compliance with JSON as an official subset.

という記載があります。

[Ansible] エラー「paramiko is not installed: No module named 'paramiko'」の原因と対策

■ エラーの原因と対策

Ansible コントールノードに paramiko (PythonSSHクライアント実装ライブラリ)がインストールされていない環境で、Ansible を実行すると、以下のようなエラーが表示されて異常終了してしまいます。

fatal: [iosao1]: FAILED! => {"msg": "paramiko is not installed: No module named 'paramiko'"}

対策としては、paramiko をインストールします。

pip install paramiko


■ 補足

Ansible 2.8 以降でネットワークモジュール利用時に注意

Ansible 2.7 台までは、 paramiko も一緒にインストールされる構成でしたが、Ansible 2.8 から一緒にインストールされない構成になりました。

2.8 からのドキュメントにも関連の記述が追記されました。

In order to use the paramiko connection plugin or modules that require paramiko, install the required module

paramiko was included in Ansible’s requirements.txt prior to 2.8.

これでも、例えばサーバーに対して ssh コネクションプラグインで接続する場合は問題ありません。paramiko を利用しないためです。 一方で、ネットワークモジュールとセットで利用する network_cli コネクションプラグインで接続する場合は、内部で paramiko を利用するため paramiko がないと前述のようなエラーになってしまいます。

例えば以下のような Playbook です。

- hosts: iosal
  gather_facts: no
  connection: network_cli

  tasks:
    - name: show version
      ios_command:
        commands:
          - show version

2.7 からのアップデートの場合は paramiko が残る

Ansible 2.7 までのバージョンから 2.8 にアップデートすると、paramiko は残ります。 そのため、「Ansible 2.8 の環境なのに、paramiko がないエラーが出る環境と出ない環境がある」という現象が起こりえます。

  • Ansible 2.7.10 から pip install -U ansible して 2.8.0 にアップデートした場合
$ pip list
Package      Version
------------ -------
ansible      2.8.0  
asn1crypto   0.24.0 
bcrypt       3.1.6  
cffi         1.12.3 
cryptography 2.6.1  
Jinja2       2.10.1 
MarkupSafe   1.1.1  
paramiko     2.4.2    <--
pip          10.0.1 
pyasn1       0.4.5  
pycparser    2.19   
PyNaCl       1.3.0  
PyYAML       5.1    
setuptools   39.0.1 
six          1.12.0 
  • Ansible 2.8 を新規インストールした場合の pip list
$ pip list
Package      Version
------------ -------
ansible      2.8.0  
asn1crypto   0.24.0 
cffi         1.12.3 
cryptography 2.6.1  
Jinja2       2.10.1 
MarkupSafe   1.1.1  
pip          10.0.1 
pycparser    2.19   
PyYAML       5.1    
setuptools   39.0.1 
six          1.12.0 

ncclient インストールで一緒に paramiko もインストールされる

Junos モジュールなどではnetconf コネクションプラグインで接続します。そのため、NETCONF 接続 Python ライブラリである ncclient を別途インストールします。 この際、paramiko も一緒にインストールされる構成になっています。

[Ansible] Ansible 2.8 リリース、便利機能や注意点まとめ

f:id:akira6592:20190517162018p:plain

■ はじめに

2019/05/16 に Ansible 2.8.0 がリリースされました。多数のモジュールの追加や、機能追加、バグ修正機能削除機能の非推奨化、仕様変更が含まれています。

大きめなトピックとしては以下のものがあります。

この記事では、CHANGELOGPorting Guide を中心に、気になった点と関連URLをまとめます。 特性が分かるように、主観ですが「便利」「地味に便利」「ちょっと注意」というタグのようなものをつけています。

また、★ 印があるものは、2019/05/31 開催予定の Ansible Night in Osaka 2019.05でもご紹介する予定です。リモート参加枠もありますので、よろしければご参加ください。

[2019/06/01追記] 当日の発表資料です。

www.slideshare.net

特に CHANGELOG は、それでだけ見てもどういったことが分かりにくいものもありますので、関連URLも参考にしていただくと良いかと思います。Ansible 2.8 へのアップデート判断の材料になれば幸いです。

■ 変更

全体

権限昇格

ansible-galaxy

制御 / jinja2 / 変数

インベントリ

環境

ファイル

システム

Windows

ネットワーク


■ 新規プラグイン

コネクションプラグイン

インベントリプラグイン

Lookup


■ 新規モジュール

Cloud

Files

Monitoring

Net Tools

Network

Notification

Source Control

System


■ まとめ

Ansible 2.8.0 、CHANGELOGPorting Guide を中心に、気になった点と関連URLをまとめました。

基本的には便利になりますが、非推奨として残しておいたものの削除や、paramiko の非同梱化など、ちょっとした注意点もありました。

[Ansible] restconf_get モジュールで Cisco IOS XE のインターフェース情報を取得してみる

■ はじめに

Ansible 2.8 では、RESTCONF でネットワーク機器の情報を取得したり、設定を変更したりできる RESTCONF モジュールが導入されます。(本記事執筆時現在 RC段階)

この記事では restconf_get モジュールを利用して、IOS-XE へ RESTCONF でアクセスしてインターフェース情報の取得を試してみます。(後述しますが、そのままではうまくいかなかったので、暫定対処として一部コードの修正をして試しました → Ansible 2.8.2 でバグ解消済み)


■ 環境

  • Cisco IOS-XE (16.8)
    • Cisco DevNet SandBox
      • NETCONF-YANG and RESTCONF Always-On
      • netconf-yangrestconf コマンド有効
  • Ansible 2.8.0rc1
    • 本記事執筆時現在 RC段階のため、pip install ansible==2.8.0rc1 でインストール

■ 準備

Playook 実行に必要なファイルや Playbook を作成していきます。

インベントリファイル

ホスト情報を示すインベントリファイルを作成します。

  • inventory
[ios]
iosao1 ansible_host=ios-xe-mgmt.cisco.com

変数定義ファイル

インベントリファイルで定義したグループ ios が利用する変数を定義するファイルを作成します。

  • group_vars/ios.yml
ansible_connection: httpapi
ansible_httpapi_port: 9443
ansible_network_os: restconf
ansible_user: root
ansible_password: p@ss9999
ansible_httpapi_use_ssl: yes
ansible_httpapi_validate_certs: no

変数の説明

変数が多数登場しますのでそれぞれ説明します。

  • ansible_connection
  • ansible_httpapi_port
    • 利用するポートを指定します。SSH 接続では ansible_portansible_ssh_port 変数でポート番号を指定しますが、ansible_httpapi_port であることにご注意ください。
    • デフォルトは、変数 ansible_httpapi_use_ssl の値によって変わります。yes の場合は 443no の場合は 80 です。
    • ここでは、環境の都合により 9443 を指定しています。
  • ansible_network_os
    • ネットワークOSを指定します。
    • ios_configjunos_config モジュールなどのベンダー個別モジュールを利用する場合はこの変数は iosjunos といった値を指定しますが、RESTCONF モジュールを利用する場合は、このように restconf を利用します。こうすることで、restconf という httpapi プラグインが利用されます。
    • もし、例えば ios を指定した場合は unable to load API plugin for network_os ios というエラーが表示されます。
  • ansible_user
    • ユーザー名を指定します。
  • ansible_password
    • パスワードを指定します。ここではダミーの値を記載しています。必要に応じて ansible-vault で暗号化します。
  • ansible_httpapi_use_ssl
  • ansible_httpapi_validate_certs
    • SSL/TLS 接続する場合に証明書をかどうかを指定します。デフォルトは yes です。
    • ここでは、環境の都合により no を指定しています。

Playbook

処理内容を記載する Playbook を作成します。

  • restconf.yml
- hosts: ios
  gather_facts: no

  tasks:
    - name: restconf test
      restconf_get:
        path: /data/ietf-interfaces:interfaces
      register: result

    - name: debug result
      debug:
        msg: "{{ result }}"

path は Cisco DevNet Learning Labs の「Exploring IOS XE YANG Data Models with RESTCONF」や、こちらのサンプルに出てきたものを利用します。

restconf モジュールでは、root_path としてデフォルトで /restconf が定義されているため、実際には、/restconf 以降を path に指定します。


■ 実行(失敗)

それでは、Playbook を実行します。が、エラーになってしまいます。

$ ansible-playbook -i inventory restconf.yml

(...略...)
fatal: [iosao1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "code": 406, "msg": "HTTP Error 406: Not Acceptable"}
(...略...)

HTTP Error 406: Not Acceptable とのことなので、HTTP の Acceppt フィールドまわりを調査しました。

原因調査と暫定対処

Ansible ではなく Postman で同様の GET を試すと以下の結果になりました。

  • Accept: application/yang-data+json では正常に json で情報が取得できた
  • Accept: では 406: Not Acceptable になった
  • Accept フィールド自体を指定しない場合は、XML で情報が取得できた

また、lib/ansible/module_utils/network/restconf/restconf.py を見ると、以下のようになっていました。

    accept = None
    if output == 'xml':
        accept = 'application/yang.data+xml'

    connection = Connection(module._socket_path)
    return connection.send_request(None, path=path, method='GET', accept=accept)

どうやら、Accept フィールドが空になってしまっていたようです。

暫定対処として、

    accept = None

の箇所を

    accept = 'application/yang-data+json'

に修正しました。


■ 再実行(成功)

暫定対処したところで、再度同じ Playbook を実行します。

$ ansible-playbook -i inventory restconf.yml 

PLAY [ios] ***********************************************************************************************************************

TASK [restconf test] *************************************************************************************************************
 [WARNING]: Platform darwin on host iosao1 is using the discovered Python interpreter at /usr/bin/python, but future installation
of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information.

ok: [iosao1]

TASK [debug result] **************************************************************************************************************
ok: [iosao1] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": false, 
        "failed": false, 
        "response": {
            "ietf-interfaces:interfaces": {
                "interface": [
                    {
                        "description": "DON'T TOUCH ME", 
                        "enabled": true, 
                        "ietf-ip:ipv4": {
                            "address": [
                                {
                                    "ip": "10.10.20.48", 
                                    "netmask": "255.255.255.0"
                                }
                            ]
                        }, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet1", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }, 
                    {
                        "enabled": true, 
                        "ietf-ip:ipv4": {}, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet2", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }, 
                    {
                        "enabled": false, 
                        "ietf-ip:ipv4": {}, 
                        "ietf-ip:ipv6": {}, 
                        "name": "GigabitEthernet3", 
                        "type": "iana-if-type:ethernetCsmacd"
                    }
                ]
            }
        }, 
        "warnings": [
            "Platform darwin on host iosao1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information."
        ]
    }
}

PLAY RECAP ***********************************************************************************************************************
iosao1                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

こんどは正常にインターフェース情報を取得できました。

※ちらっと表示されている Python interpreter についての WARNING の詳細は こちらをご参照ください。


■ まとめ

Ansible 2.8 で導入される restconf_get モジュールで、Cisco IOS-XE のインターフェース情報を取得してみました。 (一部コード修正が必要だった点は、他の対応方法があるのかなどの点は現時点では不明です。)

RESTCONF が有効であれば、他のベンダーの機器にも使える雰囲気がします。ベンダーごとにモジュールを使い分けなくて良い点は便利そうだと感じました。

また、Cisco IOS-XE では、RESTCONF が使えるようになってきているようなので、今後 RESTCONF は自動化の手段の選択肢になり得るのではないかと思います。

[2019/05/27 追記] 少しに気なる issue を見つけました。

restconf httpapi plugin Media Type · Issue #56680 · ansible/ansible · GitHub

[2019/09/06 追記] Ansible 2.8.2 で修正されていることを確認しました。 ansible/CHANGELOG-v2.8.rst at stable-2.8 · ansible/ansible · GitHub

Fix media type of RESTCONF requests.

[2019/09/09 追記] 設定変更編を投稿しました。

tekunabe.hatenablog.jp

[Batfish] JANOG43.5 で「ネットワークコンフィグ分析ツール Batfish との付き合い方」という発表をしてきました

■ はじめに

2019/04/26 に開催された JANOG43.5 Interim Meeting で「ネットワークコンフィグ分析ツール Batfish との付き合い方」という発表をさせていただきました。

janog.connpass.com

togetter.com

Batfish は、ネットワーク機器のコンフィグのさまざまな分析、検証ができるオープンソースのツールです。たとえば、経路やACL、NTP設定などの妥当性を確認できます。 コンフィグファイルを読み込んで処理するため、実機に接続する必要はありません。本発表では Batfish の概要と、ツール調査の中で見えてきた使いどころなど、Batfish との付き合い方をお伝えします。

JANOG と Batfish という点では、JANOG 43 で「Batfishというconfigテストツールの可能性」という発表がありました。今回私からは、少し違う視点でお話させていただきました。


■ 発表内容

前半で、Batfish はネットワーク機器のコンフィグの さまざまな分析、検証ができるツールであることをお伝えしました。検証のサンプルとして、ルーターのスタティックルートを削除する前後で、どのような到達性差分がでるかを検出数する例を取り上げました。

https://image.slidesharecdn.com/20190426janog43-190426053145/95/batfish-11-638.jpg?cb=1556284514

後半では、学習の仕方、バグと思った時の調べ方、使い所をお伝えしました。 使い所としては「特定の機能を、事前に、網羅的に」検証するという3つの観点でご紹介しました。

こちらのブログの内容からもヒントをいただきました。 www.intentionet.com

資料

発表に使用した資料はこちらです。

www.slideshare.net

togetter は 2019-04-26 15:18:06 から(ツイートありがとうございます!) togetter.com

動画

アーカイブ動画はこちら(1ヶ月程度の公開予定)です。私の発表は 20:15 頃からです。

www.youtube.com

いただいたフィードバック

発表直後の質疑応答の際に、以下のコメントをいただきました。(ありがとうございます!)

L2のコンフィグは試したことがなかったのでありがいコメントでした。

確かに、デフォルトだと各ノードのコンフィグのIFのL3情報をもとにして暗黙的な接続をしてトポロジを作るので、L2が入ると確かにどうなるんだろう、という思いがありました。 試せていませんが、layer1_topology.json という定義で明示的にトポロジを定義できるかも知れません。

tiwtter での反応 (ありがとうございます!)


■ 他の発表

他の方の発表も興味深いものばかりでした。

「RESTで休めない話!」にあった、自動化のユーザーインターフェースとして Google フォームを使っていて、GAS などでさまざまなことをキックする仕組みが印象てきでした。


JANOG 44は神戸

次回 JANOG 44 は、2019/07/24-2 に神戸で開催されます。次回も何かしらのかたちで参加する予定です。

https://www.janog.gr.jp/meeting/janog44/


参考: 私と JANOG

[Batfish] question で利用するノードやインターフェースなどの指定方法

question とは

ネットワークコンフィギュレーション分析ツール「Batfish」の Python ライブラリである pybatfishでは、question という形で、コンフィグに対してさまざまな検証を行います。question は Available questions というページにまとめられています。

公式チュートリアルJupyter Notebookを見ていくと、たとえば以下のような指定があります。

ert = bfq.traceroute(startLocation="as3core1[Loopback0]", headers=headers).answer().frame()

startLocation の指定は、雰囲気では、ノード as3core1 の インターフェース Loopback0 であろう、ということが読み取れますが、実際にどのような指定ができるのかは分かりません。 pybatfish のドキュメント(上記例の場合は、traceroute)を見ても、詳細は記載されていません。

Grammar for rich parameter types

これらの情報は、以下のページにまとめられています。

batfish/Parameters.md at master · batfish/batfish · GitHub

前述の例の startLocation の場合、Location Specifierを見ると、どのような指定ができるのかを確認できます。

参考

tekunabe.hatenablog.jp

[Ansible] 「ポート管理表+パラメータ表+Jinja2テンプレートから、L2SWのConfigを自動生成してみた」を Ansible で

■ はじめに

以下の記事(以下、元記事)を拝見し、Ansible でもできるかなと思って試してみました。

qiita.com

開発中の Ansible 2.8 で導入予定の read_csv モジュールを利用しています。現在(2019/04/02)安定版の Ansible 2.7 系では利用できませんのでご注意ください。read_csv モジュール を利用しない方法もできると思いますが、少し複雑になるでしょう。

  • Ansible devel バージョン (2019/04/02時点)


■ 用意するもの

ポート管理表

元記事と同じものです。

  • port_list_hqaccess1.csv
port_no,speed,duplex,mode,vlan,portfast,status,description
FastEthernet0/1,auto,auto,access,100,o,x,To PC1
FastEthernet0/2,auto,auto,access,100,o,x,To PC2
FastEthernet0/3,auto,auto,access,100,o,x,
FastEthernet0/4,auto,auto,access,100,o,x,
FastEthernet0/5,auto,auto,access,100,o,x,To PC3
FastEthernet0/6,auto,auto,access,100,o,x,To PC4
FastEthernet0/7,auto,auto,access,100,o,x,
FastEthernet0/8,auto,auto,access,100,o,x,
GigabitEthernet0/1,1000,full,access,100,o,x,To hqdist1 Gi0/0/1
GigabitEthernet0/1,1000,full,access,100,o,x,To hqdist2 Gi0/0/1

パラメータ表

元記事と同じものです。

  • parameter_list_hqaccess1.csv
hostname,hardware,secret,username,password,vlan_num,vlan_desc,ip_address,subnet,default_gw,ntp_server
hqdist1,catalyst2960,test,test,cisco,100,<< Server Segment >>,192.168.100.47,255.255.255.0,192.168.100.150,192.168.100.44

Jinja2

ほぼ、元記事と同じものです。

Ansibleで試すと改行が入ってほしいところに入らなかったので、少し調整しました。

  • catalyst2960_template.j2

クリックして展開する

!
no service pad
service timestamps debug datetime localtime
service timestamps log datetime localtime
service password-encryption
!
hostname {{ hostname }}
!
no logging console
enable secret {{ secret }}
!
username {{ username }} privilege 15 password {{ password }}
clock timezone JST 9
ip subnet-zero
no ip domain-lookup
ip domain-name {{ hostname }}
ip ssh version 2
!
spanning-tree mode pvst
no spanning-tree optimize bpdu transmission
spanning-tree extend system-id
!
!
{% for item in interfaces %}
interface {{ item.port_no }}
{% if item.description != '' %}
 description << {{ item.description }} >>
{% endif %}
{% if item.mode == 'access' %}
 switchport access {{ item.vlan }}
 switchport mode access
{% elif item.mode == 'trunk' %}
 switchport mode trunk
{% endif %}
{% if item.duplex != 'auto' %}
 duplex {{ item.duplex }}
{% endif %}
{% if item.speed != 'auto' %}
 speed {{ item.speed }}
{% endif %}
{% if item.status == 'x' %}
 shutdown
{% endif %}
{% if item.portfast == 'o' %}
 spanning-tree portfast
{% endif %}
!
{% endfor %}
!
interface Vlan1
 no ip address
 no ip route-cache
 shutdown
!
interface Vlan{{ vlan_num }}
 description {{ vlan_desc }}
 ip address {{ ip_address }} {{ subnet }}
 no ip route-cache
!
ip default-gateway {{ default_gw }}
no ip http server
no ip http secure-server
!
logging 192.168.100.107
snmp-server community C1sc0 RO
snmp-server host 192.168.100.107 C1sc0 
banner login ^C
============NOTICE==============
| This is test device for demo |
================================
^C
!
line con 0
line vty 0 4
 login local
line vty 5 15
 login local
!
ntp server {{ ntp_server }}
!
crypto key generate rsa modulus 2048
!
end

Playbook

元記事の config_generation.py に相当する Playbook です。

name: adjust..(略)... の2つのタスクは、元記事の Jinja2 テンプレートの変数構造に合わせるための調整です。テンプレート側を変更すれば、これらのタスクは不要になります。特に、adjust parameter variables のほうは、やや乱暴な調整をしているので、テンプレート側で調整したほうが良いかもしれません。

また、今回の要件では、Ansible を実行しているホストのローカルで動けばよいので、対象ホストは localhost、コネクション方式は local を指定しています。

  • build_template.yml
- hosts: localhost
  gather_facts: no
  connection: local
  
  vars:
    TEMPLATE: ./catalyst2960_template.j2
    PARAMETER_LIST: ./parameter_list_hqaccess1.csv
    PORT_LIST: ./port_list_hqaccess1.csv
    CONFIG_FILENAME: ./config_hqaccess1.txt
  
  tasks:
    - name: read interfaces
      read_csv:
        path: "{{ PORT_LIST }}"
      register: interfaces_temp
    
    - name: read params
      read_csv:
        path: "{{ PARAMETER_LIST }}"
      register: params_temp

    - name: adjust interface variable
      set_fact:
        interfaces: "{{ interfaces_temp.list }}"

    - name: adjust parameter variables
      set_fact:
        "{{ item.key }}": "{{ item.value }}"
      loop: "{{ lookup('dict', params_temp.list[0]) }}"

    - name: build template
      template:
        src: "{{ TEMPLATE }}"
        dest: "{{ CONFIG_FILENAME }}"


■ 実行

Playbookを実行します。インベントリファイルは用意せず、-i オプションでは -i localhost, のよううに host_list の形式で指定しています。

$ ansible-playbook -i localhost, build_template.yml

PLAY [localhost] *******************************************************************************************************

TASK [read  interfaces] ***********************************************************************************************************
ok: [localhost]

TASK [read params] ***********************************************************************************************************
ok: [localhost]

TASK [adjust interface variable] ***************************************************************************************
ok: [localhost]

TASK [adjust parameter variables] **************************************************************************************
ok: [localhost] => (item={'key': u'username', 'value': u'test'})
ok: [localhost] => (item={'key': u'subnet', 'value': u'255.255.255.0'})
ok: [localhost] => (item={'key': u'hostname', 'value': u'hqdist1'})
ok: [localhost] => (item={'key': u'secret', 'value': u'test'})
ok: [localhost] => (item={'key': u'default_gw', 'value': u'192.168.100.150'})
ok: [localhost] => (item={'key': u'hardware', 'value': u'catalyst2960'})
ok: [localhost] => (item={'key': u'vlan_num', 'value': u'100'})
ok: [localhost] => (item={'key': u'vlan_desc', 'value': u'<< Server Segment >>'})
ok: [localhost] => (item={'key': u'ntp_server', 'value': u'192.168.100.44'})
ok: [localhost] => (item={'key': u'password', 'value': u'cisco'})
ok: [localhost] => (item={'key': u'ip_address', 'value': u'192.168.100.47'})

TASK [build template] **************************************************************************************************
changed: [localhost]

PLAY RECAP *************************************************************************************************************
localhost                  : ok=5    changed=1    unreachable=0    failed=0    skipped=0

できあがったコンフィグファイルです。

クリックして展開する

!
no service pad
service timestamps debug datetime localtime
service timestamps log datetime localtime
service password-encryption
!
hostname hqdist1
!
no logging console
enable secret test
!
username test privilege 15 password cisco
clock timezone JST 9
ip subnet-zero
no ip domain-lookup
ip domain-name hqdist1
ip ssh version 2
!
spanning-tree mode pvst
no spanning-tree optimize bpdu transmission
spanning-tree extend system-id
!
!
interface FastEthernet0/1
 description << To PC1 >>
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/2
 description << To PC2 >>
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/3
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/4
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/5
 description << To PC3 >>
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/6
 description << To PC4 >>
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/7
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface FastEthernet0/8
 switchport access 100
 switchport mode access
 shutdown
 spanning-tree portfast
!
interface GigabitEthernet0/1
 description << To hqdist1 Gi0/0/1 >>
 switchport access 100
 switchport mode access
 duplex full
 speed 1000
 shutdown
 spanning-tree portfast
!
interface GigabitEthernet0/1
 description << To hqdist2 Gi0/0/1 >>
 switchport access 100
 switchport mode access
 duplex full
 speed 1000
 shutdown
 spanning-tree portfast
!
!
interface Vlan1
 no ip address
 no ip route-cache
 shutdown
!
interface Vlan100
 description << Server Segment >>
 ip address 192.168.100.47 255.255.255.0
 no ip route-cache
!
ip default-gateway 192.168.100.150
no ip http server
no ip http secure-server
!
logging 192.168.100.107
snmp-server community C1sc0 RO
snmp-server host 192.168.100.107 C1sc0 
banner login ^C
============NOTICE==============
| This is test device for demo |
================================
^C
!
line con 0
line vty 0 4
 login local
line vty 5 15
 login local
!
ntp server 192.168.100.44
!
crypto key generate rsa modulus 2048
!
end


■ まとめ

Ansible と Jinja2 を利用して、パラメータとなる CSV からコンフィグを生成させることができました。 Ansible が備える変数管理の仕組みを活用すれば、もっと応用ができるようになると思います。

今回のように、Jinja2 内の制御文があまり複雑でない場合や、他の処理を組み合わせなくて良い場合は Ansible でもよさそうです。一方で、もっと準備に複雑な処理を必要とする場合は、元記事のように Python などで書くのが良いと思います。