■ はじめに
以下の記事(以下、元記事)を拝見し、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 などで書くのが良いと思います。