（ほぼ）はじめてのあんしぶる

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/Ansible?src=hash&amp;ref_src=twsrc%5Etfw">#Ansible</a> Advent Calendar の2日目も日本鰻師同好会のメンバーによる投稿でした。3日目は <a href="https://twitter.com/mnagaku?ref_src=twsrc%5Etfw">@mnagaku</a> さんなのですが、あれ？長久さんも鰻師だっけかな？(そろそろ怒られるσ^_^;<a href="https://t.co/Am1Axp24Ep">https://t.co/Am1Axp24Ep</a></p>&mdash; Osamu Habuka (@habuka036) <a href="https://twitter.com/habuka036/status/936796640454656002?ref_src=twsrc%5Etfw">2017年12月2日</a></blockquote>

@tsukamanさんに続いて3日目を担当します、皆さん、はじめまして、@mnagakuです。鰻は「ふるさと納税で入手する」派です。安心してください、鰻師ではありません。ただ、各方面に害悪をまき散らすという意味では、同程度に悪質かも知れません。

僕は冷酷なビジネスマンなので、Ansible使ってる層に「僕も仲間だよ」と取り入るために、2ヶ月前からplaybookを書くようになりました。心の底ではAnsibleで冪等とか信じていませんが、仲良くしてください。宜しくお願いします。

今日は、Ansible初心者の僕が、直近に書いた雑なplaybook（デモ用のもので、運用を想定してません）を題材に、てきとーなことを書きます。初心者なので、インベントリファイルとかロールとか知りません。playbookでやり切ります。その分、構成が単純なので、これから入門したい人には、分かりやすいかも知れません。でも、最初に変なクセを覚えてしまって、Ansible警察に捕まるかも知れません。ご注意ください。

# ios_config

Ansibleにはいろいろモジュールがあって、[プリンタ](https://qiita.com/habuka036/items/3ceff8a91daec2a90444)をはじめ、さまざまな機器の操作が行えます。ネットワーク機器もサポートされており、Cisco製品にもiosモジュールでアクセスできます。

例えば、無線AP製品のAironet1140を設定するために以下のようなplaybookを書いて使っています。

```Yaml:aironet-setup.yml
---
- hosts: all
  connection: local
  gather_facts: no

  tasks:

    - telnet:
        host: "{{ inventory_hostname }}"
        user: "{{ cisco_default }}"
        password: "{{ cisco_default }}"
        login_prompt: "Username: "
        prompts:
          - "[>|#|Password:|\\[512\\]:]"
        command:
          - enable
          - "{{ cisco_default }}"
          - configure terminal
          - ntp server ntp.nict.jp
          - clock timezone JST 9
          - snmp-server community muninconnect RO
          - "username {{ set_username }} password {{ set_password }}"
          - line vty 0 4
          - login local
          - exit
          - "hostname ap{{ inventory_hostname | regex_replace('^.*\\.(\\d*)$','\\1') }}"
          - ip domain-name cisco.com
          - crypt key generate rsa
          - 1024
          - ip ssh version 2
          - line vty 0 4
          - transport input ssh
          - "enable secret {{ set_auth_pass }}"
          - "no username {{ cisco_default }}"
      tags:
        - ssh-setup

    - ios_config:
        lines:
          - "dot11 ssid {{ ssid_g }}"
        provider: "{{ cli }}"
      tags:
        - setup

    - ios_config:
        lines:
          - authentication open
          - authentication key-management wpa version 2
          - guest-mode
          - "wpa-psk ascii {{ psk }}"
        parents: "dot11 ssid {{ ssid_g }}"
        provider: "{{ cli }}"
      tags:
        - setup

    - ios_config:
        lines:
          - encryption mode ciphers aes-ccm
          - "ssid {{ ssid_g }}"
          - no shutdown
        parents: interface Dot11Radio0
        provider: "{{ cli }}"
      tags:
        - setup

    - ios_config:
        lines:
          - "dot11 ssid {{ ssid_a }}"
        provider: "{{ cli }}"
      tags:
        - setup

    - ios_config:
        lines:
          - authentication open
          - authentication key-management wpa version 2
          - guest-mode
          - "wpa-psk ascii {{ psk }}"
        parents: "dot11 ssid {{ ssid_a }}"
        provider: "{{ cli }}"
      tags:
        - setup

    - ios_config:
        lines:
          - encryption mode ciphers aes-ccm
          - "ssid {{ ssid_a }}"
          - no shutdown
        parents: interface Dot11Radio1
        provider: "{{ cli }}"
      tags:
        - setup

  vars:
    ssid_g: igdaj11g
    ssid_a: igdaj11a
    psk: igdaj2017
    cisco_default: Cisco
    set_username: wifiuser
    set_password: wifiuserpass
    set_auth_pass: wifiuseradminpass
    cli:
      host: "{{ inventory_hostname }}"
      username: "{{ set_username }}"
      password: "{{ set_password }}"
      authorize: true
      auth_pass: "{{ set_auth_pass }}"
```

ここには、工場出荷状態の機器にtelnetで入ってsshの設定を行ってから、処理そのものがssh接続下で行われるios_configを使って設定を流し込むとゆー内容が書かれています。

が、sshクライアントは、はじめて接続するサーバに対して、fingerprintを記録するという処理を行うので、そこを自動的に処理するとかなんとか対処をしておく必要があります。

ここで、困ったことがあって、公式文書の[ios_config](http://docs.ansible.com/ansible/latest/ios_config_module.html)のとこ見ても、ヒント無しなんですね。僕が読む限り。

で、公式文書サイトは、サイト内検索も付いてないっぽいので、グーグル先生に聞いてみるわけです。日本語の記事もいくつか見つかるのですが、そもそも公式文書でウラ取りしないと、情報の賞味期限も分かんないんですが、残念ながら皆さん（読んだはずだろうけど）リンク張ってないので、その辺で見た情報を公式情報の中から探すことにします。

すると、「[NETWORK DEVICE AUTHENTICATION WITH ANSIBLE 2.3](https://www.ansible.com/blog/network-device-authentication-with-ansible-2-3)」とゆー公式blogに、

> The original Ansible 2.2 example Playbook can be re-written the following way for Ansible 2.3:
> connection: local

って書いてるのを見つけたり、

「[Error: “connecting to host <hostname> returned an error” or “Bad address”](https://docs.ansible.com/ansible/2.4/network_debug_troubleshooting.html#error-connecting-to-host-hostname-returned-an-error-or-bad-address)」とゆートラブルシューティングの文書に、

> You can tell Ansible to automatically accept the keys
> Environment variable method:
> export ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD=True

って書いてるのを見つけたり、なんとなく、Ansible2.3以降のiosモジュールは通信にparamikoを使ってんだな、みたいな雰囲気が分かってきました。

うん、HOST_KEY_AUTO_ADD=Trueするぜ。

これが、動きませんでした。他にも何か設定足さないとダメなのかも知んないですが、動きませんでした。

でも大丈夫。

「[Literate Computing for Reproducible Infrastructure](https://literate-computing.github.io/)」やってる僕に死角はないわけですよ。

![手作業を明示](https://i.gyazo.com/577ad76f11e76d7ebabe3997cc71904d.png)

作業手順はJupyterのNotebookとして書いてるので、Notebook上でplaybookを分割実行することにして、途中に手作業を明示。これで大丈夫。

やったぜ！

でもまあ、ssh-keyscanで手作業なくせそうだし、varの文字列は外から与える方法にしないとなとか、そのうち直そう。

# vyos_config

[vyos_config](https://docs.ansible.com/ansible/2.4/vyos_config_module.html)が使いたかったんです。

ちょっとイキって、vyosをdockerコンテナにしてみたんですよ。dockerはネットワーク回りイマイチなので、ソフトウェアルータとか動かないの当たり前なんですが、そこをあえて挑戦するみたいな。

で、Ansibleからvyos_configで設定流し込むとか、キメたかったわけですが、vyos_configはiosと同じで、Ansible内部でssh経由で繋ぎに行くように作られてるっぽくて、connection:dockerからはvyos_config使えないんですね。

デバイスへの接続経路と、コマンド体系は、不可分じゃないので、組み合わせが選択できるようになってたら良かったなって思いました。

しょうがないので、シェルスクリプト書いて、

```Bash:vyos-setup.vsh
#!/bin/vbash
source /opt/vyatta/etc/functions/script-template

configure


set system name-server 8.8.8.8
set system time-zone Asia/Tokyo
set service snmp community muninconnect


set nat source rule 100 outbound-interface 'eth0'
set nat source rule 100 source address '192.168.14.0/24'
set nat source rule 100 translation address masquerade

set nat source rule 200 outbound-interface 'eth0'
set nat source rule 200 source address '192.168.19.0/24'
set nat source rule 200 translation address masquerade


set service dhcp-server disabled 'false'

set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 default-router '192.168.14.3'
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 dns-server '192.168.14.3'
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 domain-name 'br14'
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 lease '3600'
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 start '192.168.14.129' stop '192.168.14.254'

set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 static-mapping ap64 ip-address 192.168.14.64
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 static-mapping ap64 mac-address b0:fa:eb:f5:3f:64

set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 static-mapping ap83 ip-address 192.168.14.83
set service dhcp-server shared-network-name WIFI_LAN subnet 192.168.14.0/24 static-mapping ap83 mac-address b0:fa:eb:f5:45:83

set service dhcp-server shared-network-name WIRE_LAN subnet 192.168.19.0/24 default-router '192.168.19.3'
set service dhcp-server shared-network-name WIRE_LAN subnet 192.168.19.0/24 dns-server '192.168.19.3'
set service dhcp-server shared-network-name WIRE_LAN subnet 192.168.19.0/24 domain-name 'br19'
set service dhcp-server shared-network-name WIRE_LAN subnet 192.168.19.0/24 lease '3600'
set service dhcp-server shared-network-name WIRE_LAN subnet 192.168.19.0/24 start '192.168.19.129' stop '192.168.19.254'


set service dns forwarding cache-size '0'
set service dns forwarding listen-on 'eth1'
set service dns forwarding listen-on 'eth2'
set service dns forwarding name-server '8.8.8.8'
set service dns forwarding name-server '8.8.4.4'


commit
save
```

そいつをコピーして実行するってplaybookになりました。

```Yaml:vyos-setup.yml
---
- hosts: vyos
  connection: docker 
  user: root
  gather_facts: no

  tasks:
    - copy: src=vyos-setup.vsh dest=/root/vyos-setup.vsh
    - command: vbash /root/vyos-setup.vsh
```

なんか、かっこわるい。

# さいごに

てな感じで、Ansibleを雑に使ってます。添削は、[mnagaku/wifi](https://github.com/mnagaku/wifi)までプルリクください。待ってまーす。

そして、このネタは、[jupyterのカレンダ](https://qiita.com/advent-calendar/2017/jupyter)の4日目に~~使い回され~~続きます。

それではスタジオの@ryurockさんお返しします。現場からは以上です。

# 追記

jupyterのnotebookを「実行できる手順書」とする「[Literate Computing for Reproducible Infrastructure](https://literate-computing.github.io/)」についてのコミュニティをはじめました。

https://www.facebook.com/groups/LiterateComputing/

に、参加申請を頂ければ、順次承認させて頂きます。これまで作成された資料の一覧や、今後の発表予定などの情報に加え、チュートリアルや情報交換のイベントもやっていきたいと思いますので、是非ご参加ください。よろしくお願いします。