The Inside Playbook
Ansible + ServiceNow パート 2:ネットワークデバイスのファクトを PyATS/Genie で解析する
2019 年 7 月 19 日、著者: JColin McCarthy
本稿は、Red Hat Ansible Automation とチケット自動化の統合についてご紹介するブログシリーズの第 2 回です。今回は、スイッチやルーターから取得したネットワーク関連ファクトを動的に ServiceNow チケットに追加する方法について説明します。まだパート 1 をご覧になっていない場合は、「 Ansible + ServiceNow パート 1:チケットのオープンとクローズ 」にアクセスしてください。
使用しているバージョンのネットワーク・オペレーティングシステムに不具合があり、そのせいで稼働率の SLA に悪影響が出ているとしたら、どうでしょうか。アップグレードのプロジェクトに予算を付けてもらえるよう上司を説得するにはどうすればよいでしょうか。どうすれば、この修正にはそれだけの価値があることを示す裏付けを提示できるでしょうか。そもそも、そうであると自分が知るためにはどうすればよいでしょうか。
それには、まず追跡可能な指標を取得するところから始めるとよいでしょう。チケットからデータマイニングすれば、不具合があるバージョンのソフトウェアを実行しているハードウェアに関連するチケットがいくつあるかを知ることができます。本稿では、以後作成されるすべてのチケットに自動で一定のファクトを追加する方法について説明します。こうしておけば、まぎれもない事実を示すデータをデバイスから直接取得して記録できます。間違いが混入することも、見過ごされてチケットが作成されずに終わることもありません。
これから、Ansible と Cisco pyATS および Cisco Genie を連携させ、JSON 形式の構造化データを返させる方法をご紹介します。これを実行すると、運用時に show コマンドの出力を取得し、任意の形式に変換できます。今回はその情報を ServiceNow にプッシュします。
ネットワークデバイスからのファクトを Ansible で解析する方法は数多くあります。本稿で紹介する内容はすべて、オープンソースで公開されている Ansible ロールの Network Engine で実現できます。しかし今回は、Cisco がスポンサーとなっている pyATS/Genie 実装を使用して、次の show version コマンドを解析します。一見して分かるように、プログラムで操作しやすい出力ではありません。
手順 1:Red Hat Ansible Tower に Python3 仮想環境を作成する
Ansible Tower 3.5 がリリースされ、Python3 仮想環境 (virtualenv) を使用すれば Python のバージョンに関係なく高い柔軟性と互換性で Playbook を実行できるようになりました。pyATS と Genie のパッケージを使用するには Python3 が必須であるため、これは非常にうれしいニュースです。まず、Python3 を実行する新しい virtualenv を作成し、すべての依存ファイルをインストールする必要があります。
su -
yum -y install rh-python36
yum -y install python36-devel gcc
scl enable rh-python36 bash
python3.6 -m venv /var/lib/awx/venv/pyats-sandbox
source /var/lib/awx/venv/pyats-sandbox/bin/activate
umask 0022
pip install pyats genie python-memcached psutil pysnow paramiko
pip install -U "ansible == 2.8
カスタム virtualenv が作成されると、Ansible Tower の Job Templates セクションに新しいフィールドが表示されます。次のドロップダウンメニューから、新しく作成した venv を選択できます。
Cisco は、ネットワーク自動化に大きく役立つ Python3 パッケージを 2 つ公開しています。それが pyATS と Genie です。pyATS は Python フレームワークとして機能し、Genie はその上で動作します。Genie では parse、learn、diff などのコマンドが使用できます。Genie を実装するには、parse_genie という Galaxy ロールをインストールし、Playbook で呼び出します。
手順 2:roles ディレクトリに requirements.yml ファイルを作成する
roles/requirements.yml
---
- name: parse_genie
src: https://github.com/clay584/parse_genie
scm: git
version: master
Ansible Tower にはデフォルトで、Git リポジトリの requirements.yml ファイル経由で動的にロールをダウンロードするシステム設定があります。そのため、CLI で Ansible Engine を使用するときのように ansible-galaxy install -r
roles/requirements.yml
コマンドを実行する必要はありません。
Ansible Tower のプロジェクトの詳細については、ドキュメントをご覧ください。
手順 3:Ansible の parse_genie ロールを呼び出す
Tower に Python 3 の virtualenv を作成し、roles/requirements.yml ファイルも準備できたら、次は Playbook の作成とテストを行います。Playbook の最初の play では、名前、Ansible の動作対象とするホスト、接続プラグインを定義し、ネットワークデバイスに対する gather_facts を無効にします。次に roles: セクションを作成し、parse_genie ロールを呼び出します。
---
- name: parser example
hosts: ios
gather_facts: no
connection: network_cli
roles:
- parse_genie
続けて tasks: を作成し、show version タスクを追加します。これにより ios_command モジュール経由で show version コマンドが実行され、出力が version という変数に格納されます。
tasks:
- name: show version
ios_command:
commands:
- show version
register: version
次の連続タスクで先ほどの show version コマンドの出力に parse_genie フィルタープラグインを適用して構造化データを作成し、その構造化データをファクトとして設定して debug を実行します。
- name: Set Fact Genie Filter
set_fact:
pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"
- name: Debug Genie Filter
debug:
var: pyats_version
手順 4:Ansible Playbook を実行する
ここまで来れば Playbook はほぼ完成なので、実行とテストを行えます。
---
- name: parser example
hosts: ios
gather_facts: no
connection: network_cli
roles:
- parse_genie
tasks:
- name: show version
ios_command:
commands:
- show version
register: version
- name: Set Fact Genie Filter
set_fact:
pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"
- name: Debug Genie Filter
debug:
var: pyats_version
パーサーはコマンド出力を取得して、JSON 形式の構造化データを作成します。これで、後ほど Playbook で使用するファクトが簡単に使えるようになりました。
手順 5:Ansible Playbook の実行内容を検証する
Playbook の実行後 (この例では Ansible Tower で実行しました) の、debug Genie Filter タスクの出力は次のようになります。
出力全体は以下のとおりです。
TASK [Debug Genie Filter] ******************************************************
ok: [192.168.161.9] => {
"msg": {
"version": {
"chassis": "WS-C3550-24",
"chassis_sn": "CAT0651Z1E8",
"curr_config_register": "0x10F",
"hostname": "nco-rtr-9",
"image_id": "C3550-IPSERVICESK9-M",
"image_type": "developer image",
"last_reload_reason": "warm-reset",
"main_mem": "65526",
"number_of_intfs": {
"FastEthernet": "24",
"Gigabit Ethernet": "2"
},
"os": "C3550 boot loader",
"platform": "C3550",
"processor_type": "PowerPC",
"rom": "Bootstrap program is C3550 boot loader",
"rtr_type": "WS-C3550-24",
"system_image": "flash:c3550-ipservicesk9-mz.122-44.SE3/c3550-ipservicesk9-mz.122-44.SE3.bin",
"uptime": "44 minutes",
"version": "12.2(44)SE3",
"version_short": "12.2"
}
}
}
手順 6:解析内容を ServiceNow チケットに統合する
次に、ServiceNow インシデントのレイアウトにフィールドを追加します。ここでは、バージョン、稼働時間、ホスト名、プラットフォーム、デバイスの種別、シリアル番号、直近のリロードの理由を、Ansible が作成するすべてのインシデントチケットに追加することにします。
ServiceNow の Web ダッシュボードで [Configure] > [Form Layout] にアクセスし、これらの新しいフィールドを追加します。
ここで、このブログシリーズのパート 1 で作成した Playbook を、table パラメーターに incident を設定して実行します。incident.record 辞書に debug を実行すると、先ほど作成したフィールド (u_device_up_time、u_ios_version など) が追加されていることが確認できます。
ServiceNow API から返される record 辞書の一部
これらの新しいフィールドは、現在作成中の Ansible Playbook で、 snow_record モジュール を使うタスクの data: セクションで使用できます。以下は、作成した Playbook の全文です。この Playbook は show version コマンドを実行し、出力を解析して、パラメーターを新しいフィールドに追加します。
---
- name: create ticket with notes
hosts: ios
gather_facts: no
connection: network_cli
roles:
- parse_genie
tasks:
- name: include vars
include_vars: incident_vars.yml
- name: show version
ios_command:
commands:
- show version
register: version
- name: Set Fact Genie Filter
set_fact:
pyats_version: "{{ version['stdout'][0] | parse_genie(command='show version', os='ios') }}"
# Example 1 showing version information
- name: Debug Pyats facts
debug:
var: pyats_version.version.version
# Example 2 showing uptime
- name: Debug Pyats facts
debug:
var: pyats_version.version.uptime
- name: Create an incident
snow_record:
state: present
table: incident
username: "{{ sn_username }}"
password: "{{ sn_password }}"
instance: "{{ sn_instance }}"
data:
priority: "{{ sn_priority}}"
u_device_up_time: "{{ pyats_version.version.uptime }}"
u_ios_version: "{{ pyats_version.version.version }}"
u_hostname: "{{ pyats_version.version.hostname }}"
u_platform: "{{ pyats_version.version.platform }}"
u_device_type: "{{ pyats_version.version.rtr_type }}"
u_serial_number: "{{ pyats_version.version.chassis_sn }}"
u_last_reload_reason: "{{ pyats_version.version.last_reload_reason }}"
short_description: "This ticket was created by Ansible"
- debug: var=new_incident.record.number
この Playbook には、返された pyATS 辞書を操作する例を 2 つ追加しました。出力が構造化されているので、必要な情報をキーを使って簡単に取得できるようになりました (たとえば、pyats_version.version.uptime をキーにすれば、システムの稼働時間を示す値が得られます)。辞書全体を見るには上記の手順 5 を参照してください。
以下のスクリーンショットは、この Playbook の出力を Red Hat Ansible Tower で表示したところです。
ServiceNow のインシデントチケットを確認すると、新しいフィールドに値が入力されています。
障害が発生すると、時に現場は大混乱となります。ネットワークの現場にいる人なら、チケットを後回しにせざるを得ないことがあるのは誰もが知っています。しかしチケットの作成と動的なファクトの入力を自動化すればこの問題は解決でき、エンジニアは障害の対応に全力で取り組めます。
終わりに
こうした取り組みを行えば、組織全体での自動化の導入を段階的に進めていくことができます。このような Ansible Playbook は情報を取得するだけであり、構成を変更するものではないため、リスクも高くありません。大規模な自動化も構成管理も必要ないので、ネットワークエンジニアにとってはちょうどいい最初の一歩と言えるかもしれません。フィルタープラグインの ios エントリーを変更して、network_cli 接続プラグインで導入された ansible_network_os 変数を使用するようにしてもよいでしょう。こうすると、同じインベントリーの 1 つの Playbook で nxos、ios、junos などを対象に実行できます。本稿では、初めての方でもわかりやすいように ios のみとしました。
次回をお楽しみに。パート 3 では、ServiceNow から Ansible Tower の API への統合を取り上げます。これを行うと、ServiceNow から自動的に Ansible Playbook を実行できるようになります。