■ はじめに
以下の記事(以下、元記事)を拝見し、Ansible でもできるかなと思って試してみました。
開発中の 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 などで書くのが良いと思います。