はじめに
以前の記事で、Pythonを使ったターミナルサーバ経由のNW機器操作例をご紹介しました。
Pythonを使ってターミナルサーバ経由でNW機器を操作する方法
今回は、このPythonコードをベースに、同じ事ができるAnsibleモジュールを自作しました。
console_commandモジュール
モジュール作成にあたり、networktocode/ntc-ansibleのntc_show_command
モジュールを参考にさせて頂きました。
実装機能は、複数のshowコマンドを実行し、標準出力するだけのシンプルなものです。
import os.path
import socket
from netmiko import redispatch
import time
HAS_NETMIKO = True
try:
from netmiko import ConnectHandler
except:
HAS_NETMIKO = False
def main():
module = AnsibleModule(
argument_spec=dict(
host=dict(required=False),
username=dict(required=False, type='str'),
password=dict(required=False, type='str', no_log=True),
port=dict(required=False),
secret=dict(required=False, type='str', no_log=True),
platform=dict(required=False),
commands=dict(type='list', required=True), # 複数のshowコマンドを実行できるよう、データ型をリストに指定
delay=dict(default=1, required=False),
provider=dict(type='dict', required=False),
),
supports_check_mode=False
)
provider = module.params['provider'] or {}
no_log = ['password', 'secret']
for param in no_log:
if provider.get(param):
module.no_log_values.update(return_values(provider[param]))
# allow local params to override provider
for param, pvalue in provider.items():
if module.params.get(param) != False:
module.params[param] = module.params.get(param) or pvalue
# AnsibleのInventory/Playbook/Variableファイルで定義された値を取得し、変数に格納
ip = module.params['host']
username = module.params['username']
password = module.params['password']
port = module.params['port']
secret = module.params['secret']
device_type = module.params['platform']
command = module.params['commands']
delay = int(module.params['delay'])
# netmikoがインストールされていない場合、Failメッセージを出力
if not HAS_NETMIKO:
module.fail_json(msg='This module requires netmiko.')
else:
# ここからは前回記事と同様
device_args = dict(
device_type = 'terminal_server',
ip = ip,
port = port,
username = username,
password = password,
secret = secret,
)
net_connect = ConnectHandler(**device_args)
net_connect.write_channel('\r')
time.sleep(1)
net_connect.write_channel('\r')
time.sleep(1)
net_connect.read_channel()
redispatch(net_connect, device_type = device_type)
if secret:
net_connect.enable()
output_list = []
for cmd in command:
output = net_connect.send_command_timing(cmd, delay_factor=delay)
output_list.append(output)
net_connect.disconnect()
# ここまで
# 実行結果をJSON形式で標準出力
results = {}
results['response_list'] = [output_list]
module.exit_json(**results)
from ansible.module_utils.basic import *
if __name__ == "__main__":
main()
自作モジュールをAnsibleが認識できるよう、[Best Practices > Directory Layout]
(https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#directory-layout)に従って`library`ディレクトリを作成し、直下に`console_command.py`を格納しました。
また、ansible.cfg
ファイルにlibrary
ディレクトリへのパスを追加しました。
[defaults]
library = /home/centos/ansible/library
ansible.cfg
の格納先は、ansible --version
コマンドのconfig file
で確認可能です。
[centos@localhost ~]$ ansible --version
ansible 2.7.9
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/centos/ansible/library']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 3.6.7 (default, Dec 5 2018, 15:02:05) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
pip(Pythonのパッケージ管理システム)を使ってansibleをインストールした場合は、ansible.cfg
が作成されないため、新規作成する必要があるようです。
Playbook
Ansible2.5以降は、CLIベースのNW機器を操作する際、コネクションプラグインnetwork_cli
を使用するのが標準的ですが、今回はNW機器への接続・ログイン処理も自作したため、local
を指定しています。
簡単のため、showコマンドの出力結果は|
で限定しています。
---
- hosts: all
gather_facts: no
connection: local
tasks:
- name: execute show command
console_command:
commands:
- show version | inc Cisco
- show running-config | section interface FastEthernet1
port: "{{ port }}"
provider: "{{ cli }}"
register: result
- name: display the output of show command
debug:
var: result.response_list
vars:
cli:
platform: "{{ platform }}"
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
secret: "{{ secret | default(omit) }}"
Inventoryファイル
今回はCisco 1812J×1台を対象機器としています。
[all]
# ターミナルサーバのIPアドレス SSHログイン用のポート番号 NW機器のenableパスワード
192.168.100.56 port=3001 secret=test
[all:vars]
platform=cisco_ios # NW機器のOS名(=netmikoのdevice_type)
username=admin # ターミナルサーバのユーザ名
password=password # ターミナルサーバのパスワード
実行結果
"result.response_list":
を見ると、2つのshowコマンド結果が問題なく表示されている事が分かります。
(ただ、改行はうまくできませんでした。。)
[centos@localhost ansible]$ ansible-playbook -i inventory_consv2 playbook_consv.yml
PLAY [all] ******************************************************************************
TASK [execute show command] *************************************************************
ok: [192.168.100.56]
TASK [display the output of show command] ***********************************************
ok: [192.168.100.56] => {
"result.response_list": [
[
"Cisco IOS Software, C181X Software (C181X-ADVIPSERVICESK9-M), Version XX.X(X)XX, RELEASE SOFTWARE (fc5)\nCopyright (c) 1986-2007 by Cisco Systems, Inc.\nuse. Delivery of Cisco cryptographic products does not imply\nA summary of U.S. laws governing Cisco cryptographic products may be found at:\nCisco 1812-J (MPC8500) processor (revision 0x400) with 118784K/12288K bytes of memory.",
"interface FastEthernet1\n description << border2 - dist1 Segment >>\n ip address 192.168.200.9 255.255.255.252\n speed 100\n full-duplex"
]
]
}
PLAY RECAP ******************************************************************************
192.168.100.56 : ok=2 changed=0 unreachable=0 failed=0
最後に
今回は最低限の機能のみ実装しましたが、参考にしたntc_show_command
モジュールには、textFSMによるパース機能や、出力結果のファイル出力機能もありますので、併せて取り込めば、かなり使えるモジュールになると思います。