■ はじめに
以下の記事(以下、元記事)を拝見し、Ansible でもできるかなと思って試してみました。
qiita.com
開発中の Ansible 2.8 で導入予定の read_csv
モジュールを利用しています。現在(2019/04/02)安定版の Ansible 2.7 系では利用できませんのでご注意ください。read_csv
モジュール を利用しない方法もできると思いますが、少し複雑になるでしょう。
- Ansible devel バージョン (2019/04/02時点)
■ 用意するもの
ポート管理表
元記事と同じものです。
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で試すと改行が入ってほしいところに入らなかったので、少し調整しました。
クリックして展開する
!
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
を指定しています。
- 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 などで書くのが良いと思います。