てくなべ (tekunabe)

ansible / network automation / 学習メモ

[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

会社の本棚に追加してほしいとリクエストした技術書42冊

はじめに

先日、会社の本棚に追加してほしい本をリクエストする機会があったので、その時私がリクエストした本を掲載します。

「このラインナップなら、あの本もでしょ!」という本もあるかと思いますが、たぶんすでに本棚にあるものだと思います。あくまで、今ないものをリクエストしました。


基礎

みんなのコンピュータサイエンス

みんなのコンピュータサイエンス

開発

テスト駆動開発

テスト駆動開発

  • 作者:Kent Beck
  • 出版社/メーカー: オーム社
  • 発売日: 2017/10/14
  • メディア: 単行本(ソフトカバー)
改訂2版 みんなのGo言語

改訂2版 みんなのGo言語

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Clean Architecture 達人に学ぶソフトウェアの構造と設計

現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

アジャイルサムライ−達人開発者への道−

アジャイルサムライ−達人開発者への道−

SCRUM BOOT CAMP THE BOOK

SCRUM BOOT CAMP THE BOOK

ソフトウェア・ファースト あらゆるビジネスを一変させる最強戦略

ソフトウェア・ファースト あらゆるビジネスを一変させる最強戦略

インフラ

インフラエンジニアの教科書

インフラエンジニアの教科書

  • 作者:佐野 裕
  • 出版社/メーカー: シーアンドアール研究所
  • 発売日: 2013/10/26
  • メディア: 単行本(ソフトカバー)
インフラエンジニアの教科書2 スキルアップに効く技術と知識

インフラエンジニアの教科書2 スキルアップに効く技術と知識

  • 作者:佐野 裕
  • 出版社/メーカー: シーアンドアール研究所
  • 発売日: 2016/08/26
  • メディア: 単行本(ソフトカバー)
UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

Kubernetes完全ガイド (impress top gear)

Kubernetes完全ガイド (impress top gear)

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

実践Terraform AWSにおけるシステム設計とベストプラクティス (技術の泉シリーズ(NextPublishing))

実践Terraform AWSにおけるシステム設計とベストプラクティス (技術の泉シリーズ(NextPublishing))

入門 監視 ―モダンなモニタリングのためのデザインパターン

入門 監視 ―モダンなモニタリングのためのデザインパターン

プロトコル

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

TCP技術入門 ――進化を続ける基本プロトコル (WEB+DB PRESS plusシリーズ)

TCP技術入門 ――進化を続ける基本プロトコル (WEB+DB PRESS plusシリーズ)

ネットワーク

ネットワークはなぜつながるのか 第2版 知っておきたいTCP/IP、LAN、光ファイバの基礎知識

ネットワークはなぜつながるのか 第2版 知っておきたいTCP/IP、LAN、光ファイバの基礎知識

  • 作者:戸根 勤
  • 出版社/メーカー: 日経BP
  • 発売日: 2007/04/12
  • メディア: 単行本(ソフトカバー)
インターネットのカタチ―もろさが織り成す粘り強い世界―

インターネットのカタチ―もろさが織り成す粘り強い世界―

パケットキャプチャの教科書 (Informatics&IDEA)

パケットキャプチャの教科書 (Informatics&IDEA)

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

ヤマハルーターでつくるインターネットVPN [第5版]

ヤマハルーターでつくるインターネットVPN [第5版]

その他

人と機械の共生のデザイン-「人間中心の自動化」を探る

人と機械の共生のデザイン-「人間中心の自動化」を探る

  • 作者:稲垣 敏之
  • 出版社/メーカー: 森北出版
  • 発売日: 2012/12/08
  • メディア: 単行本(ソフトカバー)

[Ansible] json_query フィルターの実体は JMESPath

Ansible で複雑な構造の変数から特定の値を抽出する際、selectmaplist などのフィルターを駆使することがあります。

それでもだんだん苦しくなってきたときは、json_query フィルターが便利です。

Ansible もくもく会F5編のコンテンツにもちらほら登場します。

このフィルターの実体は、JMESPathです。 (そのため、pip install jmespath のように予めインストールしておく必 要があります。)

かなり複雑な条件を指定できます。jq の書式とはまた異なるものです。

Ansible の json_query フィルターのドキュメントよりもっと詳しく知りたい場合は、JMESPath のドキュメントを参照するのが吉です。

[Terraform/ACI] Terraform の Cisco ACI Provider で APIC を設定する

はじめに

本ブログでは、これまでいくつかのAnsible と ACI 対応についての記事を書いてきましたが、 Terraform も ACI に対応していることを最近知りました。

少しだけ試してみましたのでまとめます。

環境

  • Cisco DevNet Sandbox APIC 4.1(1k)
  • Terraform v0.12.19


■ Terraform の ACI 対応

Terraform の公式ドキュメントに詳細が記載されています。カテゴリとしては、Network ではなく、Cloudという扱いのようです。

https://www.terraform.io/docs/providers/aci/index.htmlwww.terraform.io

Tenant、Bridge Domain、Subnet、Contract などのオブジェクトを扱う Data Resources や Resources がそれぞれ 40以上用意されています。

aci_any Data Source や、aci_anyaci_rest Resource もあるので、専用のものがなくても、融通が効きそうです。


■ おためし

今回は、以下のような Tenant、Bridge Domain、VRF、Subnet を作ります。

f:id:akira6592:20200114152053p:plain
各オブジェクトの作成

tf ファイルの作成

main.tfと、bd.tf の 2つのファイルを作成します。(雰囲気で分けています)

main.tf の作成

provider の定義をします。

provider "aci" {
  username = "admin"
  password = "xxxdummyxxx"
  url      = "https://sandboxapicdc.cisco.com/"
  insecure = true
}

以前の記事でも書いたとおりAPIC の認証方式には、パスワードベース認証方式と、署名ベース認証方式の2種類あります。今回は手軽さ重視でパスワードベース認証方式を利用するように、privider を定義しています。

HTTPS 接続時の証明書の検証を無効化する場合は、insecuretrue に指定します。デフォルトも true です。

その他、パラメータの説明はCisco ACI Provider の公式ドキュメントを参照してください。

bd.tf の作成

Tenant、Bridge Domain、VRF、Subnet の Resource を定義をします。

resource "aci_tenant" "test_tenant1" {
  name        = "test_tenant1"
  description = "test tenant"
}

resource "aci_vrf" "test_vrf1" {
  tenant_dn   = aci_tenant.test_tenant1.id
  name        = "test_vrf1"
  description = "test vrf"
}

resource "aci_bridge_domain" "test_bd1" {
  tenant_dn          = aci_tenant.test_tenant1.id
  name               = "test_bd1"
  description        = "test bridge domain"
  relation_fv_rs_ctx = aci_vrf.test_vrf1.name
}

resource "aci_subnet" "test_subnet1" {
  bridge_domain_dn = aci_bridge_domain.test_bd1.id
  description      = "test_subnet1"
  ip               = "10.0.3.28/27"
} 

補足

少し調べるのにつまずいたのが、Bridge Domain から VRF への関連付けの方法です。以下の部分です。

  relation_fv_rs_ctx  = aci_vrf.test_vrf1.name

aci_bridge_domain を参照しても vrf という文字が見当たらないので、対応するパラメーターは無いのかもと思っていました。 ですが、ACI 用語としての VRF は Context とも呼ばれていて、クラス名?が fvCtx であることを思いだして、relation_fv_rs_ctx で設定を試したした。

なお、よく見かけるサンプルでは、id などの参照を "${}" で囲って

  tenant_dn = "${aci_tenant.test_tenant1.id}"

のようにしていますが、

Warning: Interpolation-only expressions are deprecated

という警告が表示されるので修正しました。

初回実行

terraform init コマンドの実行

terraform init コマンドで初期化します。

実行ログ(クリックして広げる)

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aci" (terraform-providers/aci) 0.1.4...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aci: version = "~> 0.1"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
$

terraform plan コマンドの実行

terraform plan コマンドで確認します。

実行ログ(クリックして広げる)

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aci_bridge_domain.test_bd1 will be created
  + resource "aci_bridge_domain" "test_bd1" {
      + annotation                  = (known after apply)
      + arp_flood                   = (known after apply)
      + bridge_domain_type          = (known after apply)
      + description                 = "test bridge domain"
      + ep_clear                    = (known after apply)
      + ep_move_detect_mode         = (known after apply)
      + host_based_routing          = (known after apply)
      + id                          = (known after apply)
      + intersite_bum_traffic_allow = (known after apply)
      + intersite_l2_stretch        = (known after apply)
      + ip_learning                 = (known after apply)
      + ipv6_mcast_allow            = (known after apply)
      + limit_ip_learn_to_subnets   = (known after apply)
      + ll_addr                     = (known after apply)
      + mac                         = (known after apply)
      + mcast_allow                 = (known after apply)
      + multi_dst_pkt_act           = (known after apply)
      + name                        = "test_bd1"
      + name_alias                  = (known after apply)
      + optimize_wan_bandwidth      = (known after apply)
      + relation_fv_rs_ctx          = "test_vrf1"
      + tenant_dn                   = (known after apply)
      + unicast_route               = (known after apply)
      + unk_mac_ucast_act           = (known after apply)
      + unk_mcast_act               = (known after apply)
      + v6unk_mcast_act             = (known after apply)
      + vmac                        = (known after apply)
    }

  # aci_subnet.test_subnet1 will be created
  + resource "aci_subnet" "test_subnet1" {
      + annotation       = (known after apply)
      + bridge_domain_dn = (known after apply)
      + ctrl             = (known after apply)
      + description      = "test_subnet1"
      + id               = (known after apply)
      + ip               = "10.0.3.28/27"
      + name_alias       = (known after apply)
      + preferred        = (known after apply)
      + scope            = (known after apply)
      + virtual          = (known after apply)
    }

  # aci_tenant.test_tenant1 will be created
  + resource "aci_tenant" "test_tenant1" {
      + annotation  = (known after apply)
      + description = "test tenant"
      + id          = (known after apply)
      + name        = "test_tenant1"
      + name_alias  = (known after apply)
    }

  # aci_vrf.test_vrf1 will be created
  + resource "aci_vrf" "test_vrf1" {
      + annotation             = (known after apply)
      + bd_enforced_enable     = (known after apply)
      + description            = "test vrf"
      + id                     = (known after apply)
      + ip_data_plane_learning = (known after apply)
      + knw_mcast_act          = (known after apply)
      + name                   = "test_vrf1"
      + name_alias             = (known after apply)
      + pc_enf_dir             = (known after apply)
      + pc_enf_pref            = (known after apply)
      + tenant_dn              = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

$ 

もろもろオブジェクトが作成されることが確認できます。plan なのでまだ実際に作成はされれいません。

terraform apply コマンドの実行

いよいよ terraform apply コマンドで、変更を適用します。

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aci_bridge_domain.test_bd1 will be created
  + resource "aci_bridge_domain" "test_bd1" {
      + annotation                  = (known after apply)
      + arp_flood                   = (known after apply)
      + bridge_domain_type          = (known after apply)
      + description                 = "test bridge domain"
      + ep_clear                    = (known after apply)
      + ep_move_detect_mode         = (known after apply)
      + host_based_routing          = (known after apply)
      + id                          = (known after apply)
      + intersite_bum_traffic_allow = (known after apply)
      + intersite_l2_stretch        = (known after apply)
      + ip_learning                 = (known after apply)
      + ipv6_mcast_allow            = (known after apply)
      + limit_ip_learn_to_subnets   = (known after apply)
      + ll_addr                     = (known after apply)
      + mac                         = (known after apply)
      + mcast_allow                 = (known after apply)
      + multi_dst_pkt_act           = (known after apply)
      + name                        = "test_bd1"
      + name_alias                  = (known after apply)
      + optimize_wan_bandwidth      = (known after apply)
      + relation_fv_rs_ctx          = "test_vrf1"
      + tenant_dn                   = (known after apply)
      + unicast_route               = (known after apply)
      + unk_mac_ucast_act           = (known after apply)
      + unk_mcast_act               = (known after apply)
      + v6unk_mcast_act             = (known after apply)
      + vmac                        = (known after apply)
    }

  # aci_subnet.test_subnet1 will be created
  + resource "aci_subnet" "test_subnet1" {
      + annotation       = (known after apply)
      + bridge_domain_dn = (known after apply)
      + ctrl             = (known after apply)
      + description      = "test_subnet1"
      + id               = (known after apply)
      + ip               = "10.0.3.28/27"
      + name_alias       = (known after apply)
      + preferred        = (known after apply)
      + scope            = (known after apply)
      + virtual          = (known after apply)
    }

  # aci_tenant.test_tenant1 will be created
  + resource "aci_tenant" "test_tenant1" {
      + annotation  = (known after apply)
      + description = "test tenant"
      + id          = (known after apply)
      + name        = "test_tenant1"
      + name_alias  = (known after apply)
    }

  # aci_vrf.test_vrf1 will be created
  + resource "aci_vrf" "test_vrf1" {
      + annotation             = (known after apply)
      + bd_enforced_enable     = (known after apply)
      + description            = "test vrf"
      + id                     = (known after apply)
      + ip_data_plane_learning = (known after apply)
      + knw_mcast_act          = (known after apply)
      + name                   = "test_vrf1"
      + name_alias             = (known after apply)
      + pc_enf_dir             = (known after apply)
      + pc_enf_pref            = (known after apply)
      + tenant_dn              = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aci_tenant.test_tenant1: Creating...
aci_tenant.test_tenant1: Creation complete after 3s [id=uni/tn-test_tenant1]
aci_vrf.test_vrf1: Creating...
aci_vrf.test_vrf1: Still creating... [10s elapsed]
aci_vrf.test_vrf1: Creation complete after 18s [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_bridge_domain.test_bd1: Creating...
aci_bridge_domain.test_bd1: Creation complete after 5s [id=uni/tn-test_tenant1/BD-test_bd1]
aci_subnet.test_subnet1: Creating...
aci_subnet.test_subnet1: Creation complete after 2s [id=uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
$ 

無事に Tenant、Bridge Domain、VRF、Subnet が作成されました。

APIC 画面でも確認します。

f:id:akira6592:20200114150045p:plain
一通り作成された


■ 宣言的であることの確認

宣言的と手続き的

ここまでで、一通りのオブジェクトが作成されました。 Terraform の特徴を掴むために、tf ファイルを少し修正して動作を確認します。

具体的に確認したい点は、Terraform は宣言的であるという点です。tf ファイルでは、あるべき状態を宣言的に定義します。そのため、定義を削除して apply すると、差分を検出して削除処理が実行されます。

一方、Ansible の Playook は全体としては手続き型です。そのため、タスク定義を削除した場合、そのタスクは実行されないというだけで、実態は残ったままになります。

確認1: Subnet 定義の削除

db.tf の修正

まず、db.tf から、Subnet の定義を削除します。

f:id:akira6592:20200114152138p:plain
Subnet の定義は削除

resource "aci_tenant" "test_tenant1" {
  name        = "test_tenant1"
  description = "test tenant"
}

resource "aci_vrf" "test_vrf1" {
  tenant_dn   = aci_tenant.test_tenant1.id
  name        = "test_vrf1"
  description = "test vrf"
}

resource "aci_bridge_domain" "test_bd1" {
  tenant_dn          = aci_tenant.test_tenant1.id
  name               = "test_bd1"
  description        = "test bridge domain"
  relation_fv_rs_ctx = aci_vrf.test_vrf1.name
}

# Subnet の定義は削除

実行

続いて、terraform planterraform apply します。

terraform plan 実行ログ(クリックして広げる)

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aci_subnet.test_subnet1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]]
aci_tenant.test_tenant1: Refreshing state... [id=uni/tn-test_tenant1]
aci_vrf.test_vrf1: Refreshing state... [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_bridge_domain.test_bd1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aci_subnet.test_subnet1 will be destroyed
  - resource "aci_subnet" "test_subnet1" {
      - bridge_domain_dn = "uni/tn-test_tenant1/BD-test_bd1" -> null
      - ctrl             = "nd" -> null
      - description      = "test_subnet1" -> null
      - id               = "uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]" -> null
      - ip               = "10.0.3.28/27" -> null
      - preferred        = "no" -> null
      - scope            = "private" -> null
      - virtual          = "no" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

terraform apply 実行ログ(クリックして広げる)

$ terraform apply
aci_subnet.test_subnet1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]]
aci_tenant.test_tenant1: Refreshing state... [id=uni/tn-test_tenant1]
aci_vrf.test_vrf1: Refreshing state... [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_bridge_domain.test_bd1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aci_subnet.test_subnet1 will be destroyed
  - resource "aci_subnet" "test_subnet1" {
      - bridge_domain_dn = "uni/tn-test_tenant1/BD-test_bd1" -> null
      - ctrl             = "nd" -> null
      - description      = "test_subnet1" -> null
      - id               = "uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]" -> null
      - ip               = "10.0.3.28/27" -> null
      - preferred        = "no" -> null
      - scope            = "private" -> null
      - virtual          = "no" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aci_subnet.test_subnet1: Destroying... [id=uni/tn-test_tenant1/BD-test_bd1/subnet-[10.0.3.28/27]]
aci_subnet.test_subnet1: Destruction complete after 1s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
$

確認

APIC の画面で確認します。

f:id:akira6592:20200114150615p:plain:w400
Subnet が削除された
Subnet の定義が削除され、宣言的な動作を確認できました。

Ansible の Playbook の場合は、Subnet 定義のタスクを削除しても、Subnet 定義は残ったままになるはずです。

確認2: Tenant 定義ごとの削除

db.tf の修正

今度は、bd.tf を空にして、Tenant の定義ごと削除します。

f:id:akira6592:20200114152217p:plain
Tenant 定義ごと削除

実行

続いて、terraform planterraform apply コマンドを実行します。

terraform plan 実行ログ(クリックして広げる)

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aci_tenant.test_tenant1: Refreshing state... [id=uni/tn-test_tenant1]
aci_vrf.test_vrf1: Refreshing state... [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_bridge_domain.test_bd1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aci_bridge_domain.test_bd1 will be destroyed
  - resource "aci_bridge_domain" "test_bd1" {
      - arp_flood                   = "no" -> null
      - bridge_domain_type          = "regular" -> null
      - description                 = "test bridge domain" -> null
      - ep_clear                    = "no" -> null
      - host_based_routing          = "no" -> null
      - id                          = "uni/tn-test_tenant1/BD-test_bd1" -> null
      - intersite_bum_traffic_allow = "no" -> null
      - intersite_l2_stretch        = "no" -> null
      - ip_learning                 = "yes" -> null
      - limit_ip_learn_to_subnets   = "yes" -> null
      - ll_addr                     = "::" -> null
      - mac                         = "00:22:BD:F8:19:FF" -> null
      - mcast_allow                 = "no" -> null
      - multi_dst_pkt_act           = "bd-flood" -> null
      - name                        = "test_bd1" -> null
      - optimize_wan_bandwidth      = "no" -> null
      - relation_fv_rs_ctx          = "test_vrf1" -> null
      - tenant_dn                   = "uni/tn-test_tenant1" -> null
      - unicast_route               = "yes" -> null
      - unk_mac_ucast_act           = "proxy" -> null
      - unk_mcast_act               = "flood" -> null
      - v6unk_mcast_act             = "flood" -> null
      - vmac                        = "not-applicable" -> null
    }

  # aci_tenant.test_tenant1 will be destroyed
  - resource "aci_tenant" "test_tenant1" {
      - description = "test tenant" -> null
      - id          = "uni/tn-test_tenant1" -> null
      - name        = "test_tenant1" -> null
    }

  # aci_vrf.test_vrf1 will be destroyed
  - resource "aci_vrf" "test_vrf1" {
      - bd_enforced_enable     = "no" -> null
      - description            = "test vrf" -> null
      - id                     = "uni/tn-test_tenant1/ctx-test_vrf1" -> null
      - ip_data_plane_learning = "enabled" -> null
      - knw_mcast_act          = "permit" -> null
      - name                   = "test_vrf1" -> null
      - pc_enf_dir             = "ingress" -> null
      - pc_enf_pref            = "enforced" -> null
      - tenant_dn              = "uni/tn-test_tenant1" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

terraform apply 実行ログ(クリックして広げる)

$ terraform apply
aci_tenant.test_tenant1: Refreshing state... [id=uni/tn-test_tenant1]
aci_vrf.test_vrf1: Refreshing state... [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_bridge_domain.test_bd1: Refreshing state... [id=uni/tn-test_tenant1/BD-test_bd1]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aci_bridge_domain.test_bd1 will be destroyed
  - resource "aci_bridge_domain" "test_bd1" {
      - arp_flood                   = "no" -> null
      - bridge_domain_type          = "regular" -> null
      - description                 = "test bridge domain" -> null
      - ep_clear                    = "no" -> null
      - host_based_routing          = "no" -> null
      - id                          = "uni/tn-test_tenant1/BD-test_bd1" -> null
      - intersite_bum_traffic_allow = "no" -> null
      - intersite_l2_stretch        = "no" -> null
      - ip_learning                 = "yes" -> null
      - limit_ip_learn_to_subnets   = "yes" -> null
      - ll_addr                     = "::" -> null
      - mac                         = "00:22:BD:F8:19:FF" -> null
      - mcast_allow                 = "no" -> null
      - multi_dst_pkt_act           = "bd-flood" -> null
      - name                        = "test_bd1" -> null
      - optimize_wan_bandwidth      = "no" -> null
      - relation_fv_rs_ctx          = "test_vrf1" -> null
      - tenant_dn                   = "uni/tn-test_tenant1" -> null
      - unicast_route               = "yes" -> null
      - unk_mac_ucast_act           = "proxy" -> null
      - unk_mcast_act               = "flood" -> null
      - v6unk_mcast_act             = "flood" -> null
      - vmac                        = "not-applicable" -> null
    }

  # aci_tenant.test_tenant1 will be destroyed
  - resource "aci_tenant" "test_tenant1" {
      - description = "test tenant" -> null
      - id          = "uni/tn-test_tenant1" -> null
      - name        = "test_tenant1" -> null
    }

  # aci_vrf.test_vrf1 will be destroyed
  - resource "aci_vrf" "test_vrf1" {
      - bd_enforced_enable     = "no" -> null
      - description            = "test vrf" -> null
      - id                     = "uni/tn-test_tenant1/ctx-test_vrf1" -> null
      - ip_data_plane_learning = "enabled" -> null
      - knw_mcast_act          = "permit" -> null
      - name                   = "test_vrf1" -> null
      - pc_enf_dir             = "ingress" -> null
      - pc_enf_pref            = "enforced" -> null
      - tenant_dn              = "uni/tn-test_tenant1" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aci_bridge_domain.test_bd1: Destroying... [id=uni/tn-test_tenant1/BD-test_bd1]
aci_bridge_domain.test_bd1: Destruction complete after 2s
aci_vrf.test_vrf1: Destroying... [id=uni/tn-test_tenant1/ctx-test_vrf1]
aci_vrf.test_vrf1: Destruction complete after 0s
aci_tenant.test_tenant1: Destroying... [id=uni/tn-test_tenant1]
aci_tenant.test_tenant1: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 3 destroyed. 

確認

APIC の画面で確認します。

f:id:akira6592:20200114151027p:plain
test_tenant1 ごと削除された

test_tenant1 ごと削除され、宣言的な動作を確認できました。 さすがに、他の Tenant は今回の tf ファイルの管理外のため残ったままです。


おわりに

Terraform の Cisco ACI Provider を使って、簡単な設定追加、削除をためしてみました。

Ansible と比較すると、やはり宣言的である点が特徴に感じました。

Terraform は Windows からでも実行できるため、手元の業務 windows 端末から実行できる点も特徴ではないかと思います。

参考

[2021/01/23 追記] - Introduction to Terraform with Cisco ACI, Part 1 - Cisco Blogs

余談

この記事を書くときに、はてなブログMarkdownシンタックスハイライトが tf に対応していることを知りました。

ソースコードを色付けして表示する(シンタックスハイライト) - はてなブログ ヘルプ

testinfra の警告「UserWarning: Unknown ssh-ed25519 host key for xx」について

はじめに

Testinfra は、サーバーの状態をテストできる Python 製のツールです。

実行時に UserWarning: Unknown ssh-ed25519 host key for という警告に出会ったので、原因と対処についてまとめます。

前提

環境や使用したテストコードは以下のとおりです。

環境

f:id:akira6592:20200103210327p:plain
実行サーバーと対象サーバー

testinfra実行サーバーの状態

  • テスト対象サーバーのフィンガープリントは ~/.ssh/known_hosts に登録 あり
    • ssh コマンドでは警告なしでログイン可能
  • ~/.ssh/config なし

テストコード

httpd がインストールされていて、バージョンが 2.4 であることをテストするコードです。

def test_httpd_is_installed(host):
    httpd = host.package("httpd")
    assert httpd.is_installed
    assert httpd.version.startswith("2.4")


テスト実行と警告メッセージコマンド

コマンド py.test --hosts=172.16.0.10 -v test_httpd.py172.16.0.10 を対象にしてテスト実行します。

$ py.test --hosts=172.16.0.10 -v test_httpd.py 
================================================== test session starts ==================================================
platform linux -- Python 3.6.8, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 -- /home/vagrant/a2860/bin/python3.6
cachedir: .pytest_cache
rootdir: /vagrant/testinfra
plugins: testinfra-3.3.0
collected 1 item                                                                                                        

test_httpd.py::test_httpd_is_installed[paramiko://172.16.0.10] PASSED                                           [100%]

=================================================== warnings summary ====================================================
test_httpd.py::test_httpd_is_installed[paramiko://172.16.0.10]
  /home/vagrant/a2860/lib64/python3.6/site-packages/paramiko/client.py:837: UserWarning: Unknown ssh-ed25519 host key for 172.16.0.10: b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    key.get_name(), hostname, hexlify(key.get_fingerprint())

-- Docs: https://docs.pytest.org/en/latest/warnings.html
============================================= 1 passed, 1 warning in 1.20s ==============================================

タイトルにもあるように以下の警告が表示されました。

UserWarning: Unknown ssh-ed25519 host key for 172.16.0.10: b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

原因

テスト対象のフィンガープリントが登録されていないことによる警告です。

前提で記載したとおり、テスト対象サーバーのフィンガープリントは ~/.ssh/known_hostsに登録してあるため、ssh コマンドでは警告なしでログイン可能でした。 にもかかわらず、登録されていない扱いなのは、testinfra のデフォルト Connection backendsが、paramikoPythonSSHクライアント)であることに起因しているようです。

対処

対処方法1: Connection backends を ssh に変更する

--hosts オプションの指定の仕方で、Connection backends を ssh に変更できます。ssh に変更することで、~/.ssh/known_hosts を参照するため、警告が出ずに実行できます。

テスト実行

$ py.test --hosts=ssh://172.16.0.10 -v test_httpd.py
================================================== test session starts ==================================================
platform linux -- Python 3.6.8, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 -- /home/vagrant/a2860/bin/python3.6
cachedir: .pytest_cache
rootdir: /vagrant/testinfra
plugins: testinfra-3.3.0
collected 1 item                                                                                                        

test_httpd.py::test_httpd_is_installed[ssh://172.16.0.10] PASSED                                                [100%]

=================================================== 1 passed in 3.04s ===================================================

警告なしで実行できました。

対処方法2: フィンガープリントのチェックをしない(要注意)

ここまでは、SSH クライアントとしての設定ファイル ~/.ssh/config は用意しませんでした。 この設定ファイルに、フィンガープリントのチェックをしないように定義しておくと、paramiko で接続詞ても警告が出ずに実行できます。

ただし、セキュリティ上のリスクを増やす設定になるので、限定的な使用にとどめておくことをおすすめします。

  • ~/.ssh/config
host 172.16.0.10
  StrictHostKeyChecking no

テスト実行

$ py.test --hosts=172.16.0.10 -v test_httpd.py 
================================================== test session starts ==================================================
platform linux -- Python 3.6.8, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 -- /home/vagrant/a2860/bin/python3.6
cachedir: .pytest_cache
rootdir: /vagrant/testinfra
plugins: testinfra-3.3.0
collected 1 item                                                                                                        

test_httpd.py::test_httpd_is_installed[paramiko://172.16.0.10] PASSED                                           [100%]

=================================================== 1 passed in 1.08s ===================================================

警告なしで実行できました。

もし、SSH クライアント設定ファイルを ~/.ssh/config 以外のファイル名にしたば場合は、py.test コマンドの --ssh-config オプションで指定します。


おわりに

正直、paramiko~/.ssh/known_hosts を参照する挙動だと思っていたので、少しはまりました。 もしかしたら、testinfra の設定やテストコードの工夫次第で参照できるようになるかもしれません。

参考

[Ansible] "[WARNING]: Ignoring timeout(10) for ios_facts" の正体とタイムアウトの設定方法

はじめに

Ansible 2.9 から、ネットワークモジュールの fact 収集は、gather_fact の指定(デフォルト yes)に基づくように仕様変更されました。 有効の場合は、内部で ios_factseos_facts などの、ベンダー別の *_facts モジュールが呼ばれます。

tekunabe.hatenablog.jp

gather_fact によってネットワーク機器の fact 収集する場合、Playbook 実行ログに以下の警告が表示されます。(ここでは、Cisco IOS を対象にした場合を想定)

TASK [Gathering Facts] ******************************************************
[WARNING]: Ignoring timeout(10) for ios_facts

これは、私なりに解釈、整理すると以下のとおりです。

当初私は意味を気にしていなかったのですが、同僚に聞かれたので調べることにしました。

この記事では、この意味が分かるまでに検証したことをまとめます。

検証環境

Ansible 2.9.2


■ 検証1: 無視されるタイムアウトの設定はどこか

警告メッセージ

[WARNING]: Ignoring timeout(10) for ios_facts

(10)10 という設定値はどこからきているのでしょうか。

ためしに、ansible.cfgdefaults セクションの gather_timeout の値をデフォルトの 10 から 20 に変更します。

[defaults]
gather_timeout = 20

設定変更が反映されているか確認します。

$ ansible-config dump --only-changed
DEFAULT_GATHER_TIMEOUT(/home/ansible/ansible.cfg) = 20

実行する Playbook を準備します。

- hosts: ios
  gather_facts: yes   # デフオルト

  vars:
    ansible_network_os: ios
    ansible_network_connection: network_cli

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

Playbook を実行します。

$ ansible-playbook -i ../inventory.ini ios_show_test.yml 

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

TASK [Gathering Facts] *******************************************************************************************
[WARNING]: Ignoring timeout(20) for ios_facts

ok: [ios1]
...(略)...

狙い通り、timeout(20) というメッセージに変わりました。これにより、無視されるタイムアウト値は、DEFAULT_GATHER_TIMEOUTansible.cfggather_timeout などで設定可)であることが分かりました。

ansible.cfg の設定は元に戻して、次の検証にいきます。


■ 検証2: 適用されるタイムアウトの設定はどこか

それでは、どのタイムアウト値が適用されるのでしょうか。

前述の Playbook では、コネクションプラグインとして、network_cli を利用しています。そのため、公式ドキュメントの network_cli のページを確認してみます。

いくつかタイムアウト関連の設定があります。ここでは 2つの設定を検証します。

設定名 変数名 概要
persistent_connect_timeout ansible_connect_timeout 接続タイムアウト
persistent_command_timeout ansible_command_timeout コマンド実行タイムアウト

接続 timeout

先ほどの Playbook に、接続タイムアウトの変数である ansible_connect_timeout の指定を追加します。検証のため、極端に 1秒にします。

- hosts: ios
  gather_facts: yes   # デフオルト

  vars:
    ansible_network_os: ios
    ansible_network_connection: network_cli
    ansible_connect_timeout: 1   # point

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

Playbook を実行します。

$ ansible-playbook -i ../inventory.ini ios_show_test.yml 

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

TASK [Gathering Facts] *******************************************************************************************
[WARNING]: Ignoring timeout(10) for ios_facts

fatal: [ios1]: FAILED! => {"ansible_facts": {}, "changed": false, "failed_modules": {"ios_facts": {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "failed": true, "invocation": {"module_args": {"auth_pass": null, "authorize": null, "gather_network_resources": null, "gather_subset": ["all"], "host": null, "password": null, "port": null, "provider": null, "ssh_keyfile": null, "timeout": null, "username": null}}, "msg": "socket_path does not exist or cannot be found.\nSee the socket_path issue category in Network Debug and Troubleshooting Guide", "warnings": ["Platform darwin on host ios1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information."]}}, "msg": "The following modules failed to execute: ios_facts\n"}

エラーになりました。変数 ansible_connect_timeout による 接続タイムアウトの設定が適用されたようです。

コマンド実行 timeout

今度は、コマンド実行タイムアウトの変数である ansible_command_timeout の指定を追加します。検証のため、極端に 1秒にします。

- hosts: ios
  gather_facts: yes   # デフオルト

  vars:
    ansible_network_os: ios
    ansible_network_connection: network_cli
    ansible_command_timeout: 1   # point

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

Playbook を実行します。

$ ansible-playbook -i ../inventory.ini ios_show_test.yml 

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

TASK [Gathering Facts] *******************************************************************************************
[WARNING]: Ignoring timeout(10) for ios_facts

fatal: [ios1]: FAILED! => {"ansible_facts": {}, "changed": false, "failed_modules": {"ios_facts": {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "failed": true, "invocation": {"module_args": {"auth_pass": null, "authorize": null, "gather_network_resources": null, "gather_subset": ["all"], "host": null, "password": null, "port": null, "provider": null, "ssh_keyfile": null, "timeout": null, "username": null}}, "msg": "command timeout triggered, timeout value is 1 secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide.", "warnings": ["Platform darwin on host ios1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information."]}}, "msg": "The following modules failed to execute: ios_facts\n"}

エラーになりました。エラーメッセージを見ると

command timeout triggered, timeout value is 1 secs

とあります。変数 ansible_command_timeout による 接続タイムアウトの設定が適用されたようです。


まとめ