ネットワークエンジニアが楽しく障害対応するための技術

# はじめに
この記事はシスコの同志による [Cisco Systems Japan Advent Calendar 2018](https://qiita.com/advent-calendar/2018/cisco) の 13 日目として投稿されました。

- 2017年版: https://qiita.com/advent-calendar/2017/cisco
- 2018年版: https://qiita.com/advent-calendar/2018/cisco

このネタは、わたしが自宅で使うために考えました。

# TL;DR
- ルータやスイッチの設定変更を行うと **ネットワーク機器が自動的に通信の正常性を確認します**
- 誰かから「なんかネットワークの調子悪くない？」と刺される前に、ネットワークの障害をお知らせします
    - メールでアラートを送るなんて生易しいもんじゃない、**[緊迫感のある BGM](https://www.youtube.com/watch?v=p8f2pMxXJwA) をスピーカーから流します**
        - **テンションが上ります**
        - **「いま障害対応中なんだ、そっとしておこう」という周囲の理解が得られる可能性があります**
- 楽しくトラブルシューティングできます！

# デモ
このデモでは（オペミスを想定して）インターネット行きのインターフェイスを `shutdown` します。

画面右下に見えている灰色の物体（スピーカー）は Google Home mini です。クリックして再生してください（**必ず音が流れる環境で再生してください**）

<a href="https://vimeo.com/306021985"><img width="962" alt="クリックして再生" src="https://qiita-image-store.s3.amazonaws.com/0/214716/50b92cee-29a4-f3bf-5ae3-d678f5c6f0eb.png"></a>

なお、権利上の問題があるので、障害発生時の音は差し替えました🤐
Future Gladiator by Kevin MacLeod is licensed under a Creative Commons Attribution license (https://creativecommons.org/licenses/by/4.0/)

# フロー
1. 人間がネットワーク装置に対して設定変更を行います<br><img width="500" alt="ルータやスイッチに対して設定変更を行います" src="https://qiita-image-store.s3.amazonaws.com/0/214716/d432866f-8b93-d1f6-9397-2e71c4bc42d6.png">
1. ネットワーク装置は自分自身の設定が変更されたことに気づいて、装置内で Python スクリプトを実行します<br><img width="200" alt="ネットワーク装置が自分自身の設定が変更されたことに気づいて、装置内で Python スクリプトを実行します" src="https://qiita-image-store.s3.amazonaws.com/0/214716/426606e2-bc41-d199-751f-68e9583ba581.png">
1. Python スクリプトは特定の Web サイトへの到達性を確認します<br><img width="500" alt="Python スクリプトは特定の Web サイトへの到達性を確認します" src="https://qiita-image-store.s3.amazonaws.com/0/214716/61ceee59-5ef7-93d7-b31d-e8bdafafecd7.png">
1. 到達性に問題があると Google Home のスピーカーを使って障害発生をお知らせします<br><img width="500" alt="到達性に問題があると Google Home (mini) のスピーカーを使って障害発生をお知らせします" src="https://qiita-image-store.s3.amazonaws.com/0/214716/e4a98989-4736-3e26-a69b-527dcb9ae16a.png">
1. 楽しくトラブルシューティングしましょう<br><img width="300" alt="楽しくトラブルシューティングしましょう" src="https://qiita-image-store.s3.amazonaws.com/0/214716/65349777-2d66-4ec3-9d80-21eb13a3669e.png">

# 背景にある技術

## EEM (Embedded Event Manager)

EEM はシスコのルータやスイッチのオペレーティングシステム IOS, IOS-XE に搭載されている機能の 1 つです。EEM を使うと管理者は**「〜が起こったら〜を実行する」**という判断や処理の自動化を装置に実装することができます。非常にシンプルな機能ですが、以下のようなメリットが挙げられます。

- 外部のサーバに頼らず、ネットワーク装置だけで判断→処理を完結できる (On-Box; Edge Computing)
- ルーティングプロトコルなどで標準化された動作を逸脱するようなカスタム機能を柔軟に実装できる (Flexible)
    - 例）タイマーに頼らないルーティング制御、冗長系への切り替え
- 「ネットワーク特有」のイベントをトリガに設定することができる (Network Programmability)
    - 例）ルーティングテーブルやインターフェイスカウンタ

EEM では一般的に「〜が起こったら」の部分を **Event Detector**, 「〜をする」の部分を **Action** と表現し、以下のような組み合わせが可能です。

<img width="993" alt="EEM Event Detectors and Actions" src="https://qiita-image-store.s3.amazonaws.com/0/214716/206326b3-7673-31e4-232f-cde8093aa58b.png">

この記事では

- Event Detector: **Syslog で `SYS-5-CONFIG_I` を検出したら**
- Action: **Python スクリプトを実行する**

という組み合わせによって**「装置自身が設定の変更を検知して、Python スクリプトを実行する」**自動化を実現しています。

## Guest Shell, On-box Python

Guest Shell はシスコのルータやスイッチのオペレーティングシステム IOS-XE 16.5 からサポートされた機能の 1 つで、LXC (Linux Container) を使うことでユーザが自由に使える Linux 環境を提供する機能です。

[Cisco IOS XE Everest 16.6.x プログラマビリティ コンフィギュレーション ガイド](https://www.cisco.com/c/ja_jp/td/docs/ios-xml/ios/prog/configuration/166/b_166_programmability_cg/b_166_programmability_cg_chapter_011.html):
> ゲスト シェルは、仮想化された Linux ベースの環境であり、Cisco デバイスの自動制御と管理のための Python アプリケーションを含む、カスタム Linux アプリケーションを実行するように設計されています。ゲスト シェルを使用して、サードパーティ製 Linux アプリケーションをインストール、更新、および操作することもできます。

機種や搭載するメモリ量によって Guest Shell 機能のサポート有無は異なりますが、[Cisco ISR 1111X](https://www.cisco.com/c/en/us/products/collateral/routers/1000-series-integrated-services-routers-isr/datasheet-c78-739512.html) のような拠点向け小型ルータでもサポートされています。

```shell
Router#show version | i IOS XE
Cisco IOS XE Software, Version 16.10.01a
Router#guestshell run bash
[guestshell@guestshell ~]$ uname -a
Linux guestshell 4.4.155 #1 SMP Wed Oct 3 17:09:19 PDT 2018 x86_64 x86_64 x86_64 GNU/Linux
[guestshell@guestshell ~]$ dohost "sh ver | i IOS XE"

Cisco IOS XE Software, Version 16.10.01a

```

このコンテナ環境には Python の実行環境が含まれており、パッケージマネージャ `pip` すら用意されています。

```shell
[guestshell@guestshell ~]$ pip install requests
```

この記事では、Guest Shell を使うことで**「Web サイトへの到達性を確認し、問題があれば発報する Python スクリプトをルータ上で実行する」**ことを実現しています。

## Google Home as a Speaker

この記事では、障害発生を管理者に知らせるために「スピーカーから音を鳴らす」ことに挑戦します。一番テンションが上がるはずです。ところで、ネットワークに繋がっているスピーカーといえば **スマートスピーカー** が思いつきます。

一般的なスマートスピーカーは、ユーザの発話をきっかけとしてクラウドと連携しながら処理を行います。具体的には

- ユーザが「ねぇ〇〇、今日の天気は？」と発話する
- 「ねぇ〇〇」（ウェイクワード）をトリガとしてユーザの音声を保存し、クラウドにアップロードする
- クラウド上で音声認識を行い、ユーザに対する返答を音声合成する
- クラウドは音声データをスマートスピーカーに返し、スピーカーから「今日の天気は晴れです」と再生される

このような流れでしょう。このようにクラウドに依存したアーキテクチャは、スマートスピーカーの単価を圧倒的に削減しつつ、柔軟に新しい機能やサービスを提供できる優れた仕組みです。

しかし今回のトピックと組み合わせることを考えると、いくつかの問題があります。

- ネットワークに障害が起こっているので、スマートスピーカーはクラウドと連携できない可能性がある
    - 音声合成をクラウドに任せることができない
- ユーザの発話をきっかけにしたくない（スマートスピーカーが自発的に発声してほしい）
    - 「ねぇ〇〇、ネットワークに障害はある？」は訊きたくないですね :P

調べてみると、Google Home (mini) は動画や音声をストリーミング再生する **Chromecast** のサブセットを含んでいるため、クラウドに頼らずに、ネットワーク接続されたスピーカーとして使うことができるそうです。そのため、この記事では Google Home (mini) を採用し、スピーカーから鳴らす音声ファイル (mp3) を予め用意しておきます。障害発生時には Google Home (mini) を Python スクリプトで制御し、音声ファイルをルータからストリーミング再生させます。

# まとめ
- EEM を使うことで「〜が起こったら〜を実行する」という判断や処理の自動化を簡単に実装することができます
- Guest Shell を使うことで、ルータやスイッチの内部で Python スクリプトを実行することができます
- 以上の 2 つを組み合わせると、ネットワーク装置のイベントに応じて非同期的に汎用的な Python スクリプトを実行することができます
    - これまでに考えられなかったような新しい機能や付加価値につながるかもしれません！
- BGM の力は偉大です

# 参考
## 設定方法やソースコード

この記事で作成したプログラムは [GitHub](https://github.com/mochipon/networkchecker) で公開しています。

<details><summary>ここをクリックすると導入手順の例が表示されます</summary><div>

**環境**

```
Router#sh ver | i IOS XE
Cisco IOS XE Software, Version 16.10.01a
```

**Guest Shell 及びネットワークの設定**

```
Router#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Router(config)#iox
Router(config)#app-hosting appid guestshell
Router(config-app-hosting)#app-vnic gateway0 virtualportgroup 0 guest-interface 0
Router(config-app-hosting-gateway0)#guest-ipaddress 192.168.35.2 netmask 255.255.255.0
Router(config-app-hosting-gateway0)#app-default-gateway 192.168.35.1 guest-interface 0
Router(config-app-hosting)#name-server0 8.8.8.8
Router(config-app-hosting)#exit
Router(config)#int virtualportgroup 0
Router(config-if)#ip address 192.168.35.1 255.255.255.0
Router(config-if)#ip nat inside
Router(config)#int gi 1
Router(config-if)#description Outside (Internet)
Router(config-if)#ip address dhcp
Router(config-if)#ip nat outside
Router(config-if)#no shutdown
Router(config)#int gi 2
Router(config-if)#description Inside (LAN)
Router(config-if)#ip address 192.168.101.1 255.255.255.0
Router(config-if)#ip nat outside
Router(config-if)#no shutdown
Router(config)#ip access-list extended ACL_ALLOWNAT2LAN
Router(config-ext-nacl)#permit ip 192.168.35.0 0.0.0.255 192.168.101.0 0.0.0.255
Router(config-ext-nacl)#ip access-list extended ACL_ALLOWNAT2WAN
Router(config-ext-nacl)#permit ip 192.168.35.0 0.0.0.255 any
Router(config-ext-nacl)#exit
Router(config)#ip nat inside source list ACL_ALLOWNAT2LAN interface GigabitEthernet2 overload
Router(config)#ip nat inside source list ACL_ALLOWNAT2WAN interface GigabitEthernet1 overload
Router(config)#ip nat inside source static tcp 192.168.35.2 45114 192.168.101.1 45114 extendable
Router(config)#end
```

**Guest Shell の有効化**

```
Router#guestshell enable
Interface will be selected if configured in app-hosting
Please wait for completion
guestshell installed successfully
Current state is: DEPLOYED
guestshell activated successfully
Current state is: ACTIVATED
guestshell started successfully
Current state is: RUNNING
Guestshell enabled successfully
```

**Guest Shell 内に Python スクリプトを設置**

```
Router#guestshell
[guestshell@guestshell ~]$ sudo yum install -y unzip
[guestshell@guestshell ~]$ wget -O networkchecker-master.zip https://codeload.github.com/mochipon/networkchecker/zip/master
[guestshell@guestshell ~]$ unzip networkchecker-master.zip
```

**EEM の設定**

```
Router#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
Router(config)#event manager applet config-check
Router(config-applet)# event syslog pattern "SYS-5-CONFIG_I"
Router(config-applet)# action 1.0 cli command "enable"
Router(config-applet)# action 2.0 cli command "guestshell run python /home/guestshell/networkchecker-master/conf_check.py --syslog --chromecast"
Router(config-applet)# action 3.0 regexp "ERROR" "$_cli_result"
Router(config-applet)# action 4.0 if $_regexp_result eq "1"
Router(config-applet)# action 5.0  syslog msg "$_cli_result"
Router(config-applet)# action 6.0 end
Router(config-applet)#end
Router#
```
</div></details>


## EEM
- [Cisco IOS フル活用への道 ： 第 1 回　EEM（Embedded Event Manager）](https://gblogs.cisco.com/jp/2017/12/ios-use-case-embedded-event-manager/)
    - @kikuta1978 さんいつもありがとうございます！
- [Programmability Configuration Guide, Cisco IOS XE Gibraltar 16.10.x - Chapter: EEM Python Module](https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/1610/b_1610_programmability_cg/eem_python_module.html)

## Guest Shell, On-box Python
- [IOS XE 上のゲスト シェルの紹介 - Cisco DevNet Learning Labs](https://learninglabs.cisco.com/modules/dnav3-intro-guestshell-jp)
    - Guest Shell の設定からサンプルスクリプトの実行まで、すべて日本語で解説しています！
    - 今回作成した Python スクリプトの大部分は、Cisco DevNet で提供されているサンプルスクリプトを流用しています
- [Cisco IOS XE Everest 16.6.x プログラマビリティ コンフィギュレーション ガイド](https://www.cisco.com/c/ja_jp/td/docs/ios-xml/ios/prog/configuration/166/b_166_programmability_cg/b_166_programmability_cg_chapter_011.html)
    - 日本語で書かれたコンフィギュレーションガイドです！
- [Programmability Configuration Guide, Cisco IOS XE Gibraltar 16.10.x - Chapter: Guest Shell](https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/1610/b_1610_programmability_cg/guest_shell.html)
- [Cisco IOS-XEでルーティングテーブルの変化を捕捉して外部サービスに投稿](https://qiita.com/kikuta1978/items/42526882b57eafe480ad)

## Google Home as a Speaker
- [GitHub: balloob/pychromecast](https://github.com/balloob/pychromecast)
- [GitHub: noelportugal/google-home-notifier](https://github.com/noelportugal/google-home-notifier)
- [Google Home開発入門 / google-home-notifier解説](https://qiita.com/SatoTakumi/items/c9de7ff27e5b70508066)

# 最後に
- こういうタイトルの記事を書いてみたかった
- 去年のアドベントカレンダーでは[Amazon Echo を使う記事](https://qiita.com/matagawa/items/313abe718970ab3d9bd9)を書いたので、今年は Google Home を使いたかった

# 免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本 Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。