てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] ネットワーク機器のコンフィグ生成ツールとしての Ansible

はじめに

Ansible にはネットワークモジュールが多数用意されていて、ネットワーク機器に接続してコマンドを実行できます。

では、機器に接続できない環境で投入コンフィグを準備するにはどのようにすればよいでしょうか。 それには template モジュールが利用できます。

ネットワーク機器そのもとは直接関係なく汎用的なものなので、テンプレートの書き方次第で Ansible が対応していないネットワーク機器のコンフィグも生成できます。

この記事では、コンフィグテンプレートと、(YAMLではなくあえて)CSVファイルに定義したパラメータを利用して、簡単なコンフィグファイルを生成する方法をご紹介します。


準備するもの

├─ configs               生成コンフィグを出力先ディレクトリ
├─ templates             コンフィグテンプレート格納ディレクトリ
│ └─ config_template.j2  コンフィグテンプレート
├─ params.csv            パラメーターCSVファイル
└─ gen_config.yml        Playbook

インベントリファイルは不要です。

CSV ファイル (params.csv

ホスト名と、VLAN、IPアドレスを定義する CSV ファイルです。1行目はヘッダーです。

hostname,vlan,address,mask
sw101,10,10.0.10.254,255.255.255.0
sw201,20,10.0.20.254,255.255.255.0

コンフィグテンプレート (templates/config_template.j2)

Ansible ではテンプレートエンジンとして Jinja2 を利用しているため Jinja2 形式で書きます。 ホスト名と vlan に IP アドレスを設定するだけの部分的なコンフィグのテンプレートです。

hostname {{ item.hostname }}

interface vlan{{ item.vlan }}
  ip address {{ item.address }} {{ item.mask }}

後述の Playbook の loop で呼び出されるため、具体的なパラメーターは item の中を指定します。

Playbook (gen_config.yml)

Playbook は以下の通りです。read_csv モジュールで、CSV ファイルの内容を読み取って変数化して、template モジュールでコンフィグファイルを生成します。 生成するコンフィグのファイル名には、CSV ファイルの hostname 列の値を利用します。

- hosts: localhost
  gather_facts: no
  connection: local

  tasks:
    - name: read csv
      read_csv:
        path: params.csv   # パラメーターCSVファイル
      register: params

    - name: generate config
      template:
        src: config_template.j2    # コンフィグテンプレートの指定
        dest: "configs/{{ item.hostname }}_config.txt"  # 出力先
      loop: "{{ params.list }}"   # CSVフィアルの行数分をループ(ヘッダー除く)


Playbook 実行

Playbook を実行します。

$ ansible-playbook -i localhost, gen_config.yml 

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

TASK [read csv] ******************************************************************************************************

ok: [localhost]

TASK [generate config] ***********************************************************************************************
changed: [localhost] => (item={'hostname': 'sw101', 'mask': '255.255.255.0', 'vlan': '10', 'address': '10.0.10.254'})
changed: [localhost] => (item={'hostname': 'sw201', 'mask': '255.255.255.0', 'vlan': '20', 'address': '10.0.20.254'})

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


確認

生成されたファイルを確認します。

$ cat configs/sw101_config.txt 
hostname sw101

interface vlan10
  ip address 10.0.10.254 255.255.255.0
$ cat configs/sw201_config.txt 
hostname sw201

interface vlan20
  ip address 10.0.20.254 255.255.255.0

無事にホストごとにパラメータが異なるコンフィグが生成されました。


さいごに

ごく簡単な例でしたが、いろいろ応用はできるかと思います。 Excel のシートと VBA などでコンフィグを生成していて、他の方法を探している場合に参考になればと思います。

ケーブルの接続先を追う時に面ファスナー(マジックテープ/ベルクロ)を使うと便利

はじめに

ケーブルを追っていく方法を紹介した以下ツイートの補足です。

写真は自宅の機器で、説明用に適当に取り繕ったものなのでご了承ください。

手で追うと間違えそうになったり、手が離せなくなったりするので。

あと、いわゆるねじりっこは使わないようにします。

途中でケーブルが束ねられていたら・・・

そのままではなかなか通しにくいですね。

束ねられている両端を手で引っ張るなどして、束ねた反対側のケーブルを特定したらそちら側に面ファスナーを移す(または追加で付ける)、のように、合わせ技を使うようかなと思います。

ラックや床や天井を経由したら難しいのでは?

こちらも難しいですね。。

個人的には、一番よく使うケースは電源ケーブルを追うときなので、基本的にはラック内で収まってます。

そもそもタグや管理表で管理されているべきでは?

本来はそうです。ただ現実としては、タグが付いていない場面に遭遇することもあるので、そのような場合は追う必要があります。

あとは、タグや管理票があっても信用できないというか、より慎重を要する場合は、現場の現状の確認したほうが良いと思います。

LAN ケーブルはちゃんとしていても、電源ケーブルはタグがなかったり、どのタップのどの口に接続されているかまで管理されていないこともあるかと思います。

参考

参考なったツイートをご紹介します。

リング通しと呼ぶ

研修やマニュアルで教わる

別のもので代用

知恵

https://twitter.com/tanke25616429/status/1223619432083148802

[Ansible] httpd.conf を編集後にファイルの検証処理を挟む方法

はじめに

Ansible の lineinfilereplace などのファイルを編集するモジュールには validate オプションを備えているものがあります。

validate オプションには編集後のファイルの妥当性を検証するコマンドを指定します。 これにより、編集に問題がなければ処理を継続、問題があれば処理を停止、のようにワンクッション挟めるため安全性を高められます。

公式ドキュメントの lineinfile モジュールの Example には、/etc/sudoers 編集後に実行する validate オプションの例があります。

この記事では、 Apache httpd の設定ファイルである、httpd.conf を validete する Playbook の例をご紹介します。


サンプル Playbook の作成

以下のような簡単な Playbook を利用します。

- hosts: web
  gather_facts: no
  become: yes

  tasks:
    - name: configure Listen Port
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen 80$'
        line: 'Listen 8080'
        validate: httpd -tf %s    # point
      notify:
        - restart httpd

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted
        enabled: yes

処理は以下のとおりです。

  • lineinfile モジュールによりhttpd.conf^Listen 80$Listen 8080 に置換
    • 変更なしなら、終了
    • 変更ありなら、次へ
  • httpd -tf コマンドにて validate する
    • 問題ありなら、エラーで終了
    • 問題なしなら、次へ

validate: httpd -tf %s について補足

validate オプションには httpd -tf %s を指定しています。

httpd コマンドの -t オプションによって、httpd.conf に問題がないか検証できます。-f オプションを併用することで検証対象のファイルを明示指定できます。

%s は Ansible 側の仕様によるものです。lineinfile などのモジュールでは、編集対象のファイルを直接編集せず、編集用に一時ファイルを作成します。その一時ファイルのパスが %s に格納されます。

そのため、httpd -t のみの指定の場合、Ansible が編集した一時ファイルを検証の対象にしないでご注意ください。


Playbook の実行

validete が OK なパターンと NG なパターンそれぞれ試します。

OK パターン

$ ansible-playbook -i inventory.ini web.yml

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

TASK [configure Listen Port] ****************************************************************************************
changed: [localhost]

RUNNING HANDLER [restart httpd] *************************************************************************************
changed: [localhost]

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

validete に問題がなかったので、処理が継続されました。

NG パターン

Playbook で指定した置換後文字列の

        line: 'Listen 8080'

        line: 'Listen XXX'

にして試します。 httpd.conf の Listen は待受ポート番号を指定する項目のため、文字列が指定されると httpd -tf %s の validate によって Syntax Check になります。

$ ansible-playbook -i  inventory.ini web.yml
PLAY [localhost] ****************************************************************************************************

TASK [configure Listen Port] ****************************************************************************************
fatal: [localhost]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "failed to validate: rc:1 error:AH00526: Syntax error on line 43 of /tmp/tmpbcDcMD:\nPort must be specified\n"}

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

failed to validate: rc:1 error:AH00526: Syntax error on line 43 of /tmp/tmpbcDcMD:\nPort must be specified\n" というエラーメッセージとともに validate が NG となり、処理がエラーで停止されました。

手動で validate した場合は、以下の実行イメージです。

$ httpd -tf <対象httpd.conf>
AH00526: Syntax error on line 43 of <対象httpd.conf>:
Port must be specified


さいごに

もし validate オプションを指定しなかった場合、httpd.conf に問題があっても、ハンドラー restart httpd が呼ばれてしまい、再起動を試みたところで失敗し、httpd が停止したままになってしまいます。

validate オプションを有効に使うことで、このような自体を未然に防げます。

参考

[Ansible] lookup plugin 名は大文字小文字を意識しておいた方が良い(2.10から区別される)

はじめに

Ansible には lookup plugin という仕組みがあり、ファイルの内容を取得したり、指定したURLの情報を取得したりできます。(一覧はこちら

たとえば、file という lookup plugin であれば、 lookup("file", "ファイル名") のような形式で指定します。

現状、lookup plugin 一覧に掲載されている lookup plugin 名は全て小文字です。Ansible 2.9 ままでは、大文字でも小文字でも関係なく呼び出せるのですが、開発中のバージョンで大文字小文字の区別をするようになりました。Ansible 2.10 に反映される予定で、Ansible 2.10 の Porting Guide にも追記されました。

挙動の確認のために試してみましたので、結果をまとめます。

動作確認環境(比較用に2つ用意)


検証用 Playbook

file lookup plugin を利用する以下の Playbook を利用します。

- hosts: localhost
  gather_facts: no
  connection: local

  tasks:
    - name: 1 all lowercase
      debug:
        msg: '{{ lookup("file", "test.txt") }}'   # すべて小文字(通常)

    - name: 2 start uppercase
      debug:
        msg: '{{ lookup("File", "test.txt") }}'   # 大文字はじめ


Playbook 実行

Ansible 2.9.4 の場合

Ansible 2.9.4 で Playbook を実行します。

$ ansible-playbook -i localhost, lookup.yml

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

TASK [1 all lowercase] *****************************************************************************************
ok: [localhost] => {
    "msg": "Hello, Ansible!"
}

TASK [2 start uppercase] ***************************************************************************************
ok: [localhost] => {
    "msg": "Hello, Ansible!"
}

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

file でも File でも正常に file lookup plugin を呼び出せて、ファイルの内容が表示されました。

Ansible 2.10.0.dev0(開発中)の場合

Ansible 2.10.0.dev0(開発中) で同じ Playbook を実行します。

$ ansible-playbook -i localhost, lookup.yml

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

TASK [1 all lowercase] *****************************************************************************************
ok: [localhost] => {
    "msg": "Hello, Ansible!"
}

TASK [2 start uppercase] ***************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "lookup plugin (File) not found"}

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

2つめのタスクの、大文字からはじめた File という指定は "lookup plugin (File) not found" というエラーになってしまいました。


さいごに

Ansible 2.10 で、lookup plugin 名の指定は、大文字小文字が区別されるようになることを確認しました。

2.9 などの stable バージョンでは区別されないので、あわてて対応する必要はありませんが、将来に備えて意識しておいたほうがよさそうです。感覚としては正しい仕様になったのかなと思います。

interface description はメタデータになり得るか

description に何をどう書いてる?

ネットワーク機器の物理や論理インターフェースの descripion にどんなことを書いているでしょうか?

  • 自/対向ホスト名
  • 自/対向システム名
  • 大文字?小文字?
  • # は使ってる?
    • 自動化時にプロンプトと誤認知してコケやすい
  • デリミタは - ? _ ?

などなど、いろいろお作法はあるかと思います。

有効活用している例

discription の書き方を工夫して、メタデータとして有効活用している例を目にしたことがありますので、ご紹介します。

みなさんがどうしてるのか気になる

こうなってくると、みなさんがどういう description を書いてるのか気になってきます。ネットワーク機器のコンフィグの中でも自由に書ける数少ない項目なので、いろいろ工夫されているのではないかと思います。

とはいえ、description にはホスト名やシステム名など、固有名詞が書かれることもあり、オープンにしづらい点もあります。

少しこじんまりと共有するために、「JANOGで description BoF(座談会のようなもの)やるのとかどうでしょう」なんて話も聞きました。なにか機会があれば聞いてみたいです。

妄想

JSONのような構造化データの形式で書いたら、活用方法広がるかな?と妄想しました。

interface Loopback99
 description {"from": "rt1", "to": "rt2"}
end

[Ansible] ansible-playbook コマンドの -l オプションで除外ホストを指定する方法

はじめに

Ansible には、対象ホストを制限するためパターンの記法があります。この記法は、Playbook 内の hosts: <pattern> にしか指定できないと思っていたのですが、ansible-playbook コマンドの -l オプション にも利用できるようです。

この記事では、ansible-playbook コマンドの -l オプションで、除外ホストを指定する方法(-l '!除外ホスト')をご紹介します。

  • 動作確認環境
    • Ansible 2.9.4


Playbook の準備

サンプルとして利用する Playbook は以下の通りです。

- hosts: ios
  gather_facts: no

  tasks:
    - name: show command test
      ios_command:
        commands:
          - show version
      register: result

    - name: debug
      debug:
        msg: "{{ result.stdout_lines[0] }}"

hosts で指定している ios は、以下のインベントリファイルの ios グループを指します。3つのホストが所属しています。

[ios]
ios1 ansible_host=10.0.1.254
ios2 ansible_host=10.0.2.254
ios3 ansible_host=10.0.3.254

別途、ansible_connetcion や認証情報を定義した変数ファイルも用意します。


Playbook の実行

-l オプション無しで実行すると、ios グループに所属する ios1ios2ios3 の3つホストが対象になります。 ここでは、 ios2 を除外するように -l オプションを指定します。

除外の指定は !除外ホスト のように、頭に ! を付けます。さらに、シェルに通常の文字列だと認識させるために、シングルクォート(') で囲います。

コマンド全体は以下の通りです。

ansible-playbook -i inventory.ini  ios_show.yml -l '!ios2'

実行結果

$ ansible-playbook -i inventory.ini  ios_show.yml -l '!ios2'

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

TASK [show command test] ******************************************************************************************
ok: [ios3]
ok: [ios1]

TASK [debug] ******************************************************************************************************
ok: [ios1] => {
    "msg": [
        "Cisco IOS XE Software, Version 16.11.01a",
        "...(略)..."
        "Configuration register is 0x2102"
    ]
}
ok: [ios3] => {
    "msg": [
        "Cisco IOS XE Software, Version 16.09.03",
        ...(略)...
        "Configuration register is 0x2102"
    ]
}

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

除外ホストとして指定した ios2 は対象とならず、ios1ios3 のみが対象となりました。


Playbook を実行せずに対象ホストを確認する

ホストパターンは様々な書き方ができるので「この書き方で意図した通の制限ができているだろうか」と不安になるかもしれません。

そんな時は ansilbe-playbook コマンドの --list-hosts オプションを併用すると便利です。Playbook は実行せずに、対象ホストを確認できます。

以下、実行例です。

$ ansible-playbook -i inventory.ini  ios_show.yml -l '!ios2' --list-hosts

playbook: ios_show.yml

  play #1 (ios): ios    TAGS: []
    pattern: ['ios']
    hosts (2):
      ios3
      ios1

ios2 が除外されていることが分かります。


さいごに

ansible-playbook コマンドで、-l (または --limit オプションでパターンを指定できると、Playbook やインベントリファイルを修正せずに、柔軟に実行対象を指定できるので、便利だと思いました。

JANOG45 Meeting in Sapporo に参加して視野を広げる必要性を感じた

はじめに

f:id:akira6592:20200126205937j:plain:w400
会場入口、靴の雪は落として入る

2020/01/22-24 に札幌プリンスホテル国際館パミールで開催された JANOG45 Meeting in Sapporo に参加してきました。

本記事では、参加した本会議の各プログラムの一覧と、いくつかピックアップして感じたことなどをまとめます。

今回は、全体的に自分が興味があるレイヤーと違うところにも目を向けるべきだなと感じることが多かったです。ツールや機器の使い方だけではなくプロトコルや概念も、機器だけなく管理情報も、といった感じです。


■ 参加したプログラム一覧

「★」は本記事ピックアップ対象のプログラムです。ブース担当がメインだったため、今回は少なめです。

Day1

Day2

Day3




■ 宣言的(Declarative)ネットワーキング

ネットワーク機器はコマンドという手続きで構成されるものなので、宣言的とはどういうことだろうと思って興味深く聞きました。

私が本プログラムに参加する前までに、かろうじて持っていた「宣言的」とは「レイヤー」としてのイメージでした。 例えば、河野さんが例にしていた「11ページを開く」であれば以下のイメージでした。

f:id:akira6592:20200126211116p:plain:w400
レイヤーとしての宣言的

あとは「ネットワーク設定自動化に利用するインプット形式の分類(範囲、処理形式、表現形式別) - てくなべ (tekunabe)」あたり。

本プログラムで聞いた話は、イメージしていたものとは違って全然知らない世界だったので、少し基礎を学んでみようと思いました。やがて、ネットワーク自動化の考え方にも活かせるようになるかもしれません。

今回は初めて宣言的プログラミングという存在を知り、Day3のあとに MARUZEN&ジュンク堂書店 札幌店に行って、「すごいHaskellたのしく学ぼう!」という本を買ってきました。


■ インフラの運用を楽にする情報管理 ~我々のベストプラクティスはこれだ!~

情報の管理のあり方として、情報を一元管理することと、IFTTT / Workflow / Provisioning Service との連携することを取り上げられていました。

情報を一元管理しておかないと、複数の部署が似たようなスプレットシートを作ってしまい、冗長管理、不整合を引き起こしてしまいます。 一元管理、できるものを用意しても、みんなが同じものをきちんと利用し続けてくれるように、運用の仕組みや教育を整備したりすることも重要だと感じました。

自動化を始める上でも、正しい構成情報がないとはじめられない思うので、情報管理は効率化の基盤だと思いました。 地味ですが重要ですね。作業そのものより、事前に構成を調べたりすることに多くの時間を要することもありますし。

時々、海外の自動化関連の資料で、Single Source of Truth (SSOT)という言葉を見かけるのですが、関連がある気がしています。

今後、情報交換をするために、「運用を楽にする情報の管理方法を追求する人々の集まり」というslackも紹介されていました。さっそく参加させていただきました。


■ 継続可能なテスト自動化を目指して

テスト自動化を始めたあと、いったん下がってしてしまった自動化適用率を上げるための組織的、技術的な様々な取り組みについて。

要所要所で、振り返り(アンケート&インタビュー)、データ収集をされてるのが素敵だと思いました。また、1週目でコスト効果を出すのは難しく、4周目で3割位の機能でコスト効果が出た、という話があり、粘り強くやっていく必要性を感じました。


ハッカソン Wrap-up

JANOG42から始まって今回が4回目となるハッカソン。ネットワークにまつわる自動化、可視化などをテーマにモノを作るイベントです。今回は Day1 の午前(開会式よりも前!)から丸一日かけて開催されました。本ハッカソンのまとめと、優勝チーム、ベストチャレンジ賞受賞チームから発表がありました。

f:id:akira6592:20200126210717j:plain:w400
チームとテーマ一覧

優勝チーム: 株式会社エヌ・ティ・ティ ネオメイト

テーマ: ネットワークの経路管理がしたい

インターフェースのリンク状態やトラフィック経路などの変化を把握するには時間がかかるので、画面から簡単に確認できるというものでした。

利用したツールは、以下のもの。

名前 今回の用途
Cytoscape (厳密にはCytoscape.js?) 経路グラフの可視化
Vue.js フロントエンドフレームワーク
Flask REST API サーバー・バックエンド
TextFSM show コマンドパーサー

デモも見れました。経路の可視化がすごい分かりやすかったです。

たまたまですが、以前私も Cytoscape.js を利用した経路の可視化を試したことがありました。うまく表示されると楽しいです。 スマホを振ってネットワークの経路を変える(動画)

ちなみに、JANOG43ハッカソンの優勝チームのテーマは「NW flowとDNS 名前解決を合わせたデータ分析」、JANOG44では「自動棚卸し」(インタビュー記事)でした。今回もそうですが、ネットワーク機器には設定変更を加えない Read 系処理が続いてるので、一定の需要や課題感があるのだなぁと感じました。

f:id:akira6592:20200126210252j:plain:w400
やりたいこと

f:id:akira6592:20200126210446j:plain:w400
構成

f:id:akira6592:20200126210631j:plain:w400]
デモ中

ベストチャレンジ賞受賞チーム: 北海道大学

テーマ: 鳥が飛んできます (ネットワークを介して鳥を撮影したい)

ベストチャレンジ賞というのは今回新設の賞。事前準備は含まず、ハッカソン当日にどれだけの成果を上げたかに焦点をあてた賞のようです。

研究のために仙台(確か)の沼に設置した鳥を撮影するカメラを、リモートからコマンド一発で撮影出来るようにしたもの。 構成などを当日にスタッフとも相談して決めたそうです。

こちらもデモが見れました。今までなかったようなテーマだったので新鮮でした。

f:id:akira6592:20200126210914j:plain:w400
鳥の事情

f:id:akira6592:20200126210840j:plain:w400
沼に設置したカメラ

f:id:akira6592:20200126210941j:plain:w400
デモ中(撮影成功!)




さいごに

2月初旬に当日の各プログラムの動画のアーカイブが公開される予定とのことですので、JANOG Meeting の twitter アカウントなどでチェックしていきたいと思います。見れなかったプログラムが多かったのでとても助かります。

twitter.com

JANOGは 41から参加させていただいていますが、今回もとても楽しい時間を過ごすことができました。

ホストの 北海道総合通信網株式会社様、株式会社ネクステック様、協賛各社様、スタッフみなさま、登壇者みなさま、ありがとうございました!


参考・おまけ

toggetter

@goto_ipv6 さん、まとめありがとうございます。

togetter.com

写真

f:id:akira6592:20200126211749j:plain:w200
会場に向かう途中、慣れない雪道をどうにかこうにか・・

f:id:akira6592:20200126211837j:plain:w200
新札幌駅で食べた味噌ラーメン

f:id:akira6592:20200126211928j:plain:w200
会場から徒歩で行ったお店の味噌ラーメン

f:id:akira6592:20200126212052j:plain:w200
会場から徒歩で行ったお店の海鮮丼

f:id:akira6592:20200126212108j:plain:w200
あなごです

f:id:akira6592:20200126213222j:plain:w200
後日訪れた小樽築港、寒い

公式フォトブック

今回初の試みとして、Facebook にたくさんの写真がアップされました。

www.facebook.com

私と JANOG

今後の JANOG