昔からネットワーク機器を多く相手にしてきたエンジニアは、 CLIで設定を作って投入することが多く、プログラミングが苦手な方が多い印象です。
近頃はネットワークも自動化の波が大きく、 APIによる設定変更に対応している機器も増えてきました。
ネットワークに詳しく、自動化等にも長けたエンジニアは、 現在も希少価値が高い印象ですので、 まず、どんな手法があるのかを知ることから始めてはいかがでしょうか。
はじめに
古くからある手法ですが、Excel関数を駆使してConfigを出力して、 Teratermマクロで投入することも立派なネットワーク自動化(効率化)です。
例えば、下記の記事のようなものです。 itchrome.blogspot.com
ただ、初期構築時ならまだしも、 商用環境の変更用ConfigをTeratermマクロで投入することは反対する人も多いと思います。 CLI操作ができる状態でTeratermマクロが止まれば、誤操作による事故のもとです。
また、Teratermマクロは書式が独特で、古くから存在するマクロは、 修正したくても解析がしにくく、手が付けられない状態になることが多い印象ですし、 〇〇を実行して××行目の値が△△だったら、のような処理を書くのも難しいです。
なので、別の自動化手法が流行っているのだと考えています。
Python
ネットワークの自動化ではPythonが多く使われています。 設定情報のやり取りなどのデータ構造はjson形式が多く使われていますが、 これがPythonの辞書型と書式が同じであるため、データの整形(パース)や、 条件分岐処理をコーディングしやすいというメリットのためと考えられます。
pexpect
pythonライブラリのひとつです。所定のプロンプトを待ち、 決まったコマンドを投入することが主要動作です。 まさにTeratermマクロのような動きをします。 下記は機器にSSHしてenableパスワードを入力し、show versionを表示します。
import pexpect device ='ssh admin@172.16.1.1' p = pexpect.spawn(device) p.expect('Password:') p.sendline('sshpw') p.expect('>') p.sendline('enable') p.expect('Password:') p.sendline('enapw') p.expect('#') p.sendline('terminal length 0') p.expect('#') p.sendline('show version') p.expect('#') result = p.after print(result.decode('utf-8'))
【メリット】作りこもうとすれば何でも作れる
【デメリット】完成まで時間がかかり、保守するのが難しい
netmiko
こちらもpythonライブラリのひとつです。 ネットワーク機器独特の「enableパスワードを入力する」ことも基本機能に含まれているため、 「enable」を入力→「Password:」というプロンプトを待つ→「パスワードを投入する」という処理を書く必要はありません。 コマンド投入完了を自動判定するため、毎回プロンプトを待つ処理を書く必要はありません。
import netmiko session = netmiko.ConnectHandler(device_type='cisco_ios', host='172.16.1.1', username='admin', password='sshpw', secret='enapw') session.enable() result = session.send_command('show version') print(result) session.disconnect()
【メリット】 簡易なため、情報収集のみなら便利
【デメリット】出力を整形する必要があるため、条件分岐処理等には不向き
Ansible
Playbookと呼ばれるYAML形式のファイルを使用して動作します。 内部の仕組みとしては、Pythonで作成された各モジュールを使用し、 SSHでネットワーク機器にアクセスして実行されます。
各モジュールは、別途インストールするものではなく、
当該Ansibleのバージョンに含まれていれば、そのまま使えます。
下記はバージョンをチェックする例です。
<認証情報(auth.yml)>
ansible_connection: network_cli ansible_network_os: ios ansible_user: admin ansible_ssh_pass: password
<Playbook>
- hosts: DEVICES gather_facts: no vars_files: - ../vars/auth.yml tasks: - name: Check Version ignore_errors: True ios_command: commands: - "show version" wait_for: - result[0] contains "16.9"
昔は対話処理(実行する場合はyを入力してください のような)ができなかったのですが、 「cli_command」というモジュールを使えば対応可能です。
条件分岐についても「when」を使えばできるのですが、 「〇と△の変数が☆で、かつ××の処理が失敗した場合のみ実行」のようなことをやろうとすると、 途端に可読性が悪くなります。 やはりドキュメント化をしっかりすべきかと思います。
when: (work1_result.failed is defined) and (work1_result.failed == true and parameter1 == True and parameter2 != True)
可読性が良いことがウリのように思うのですが、これを悪くしてしまっては本末転倒に思います。
【メリット】 手順書的な内容をシンプルに表記でき、プログラミング要素が少ない
【デメリット】複雑にしすぎると条件分岐が読み取りにくい
REST API (RESTCONF)
これが一番流行っていると思います。 HTTPで情報を取得する方法です。
REST APIは今後RESTCONFに移り変わりつつある流れですが、 Pythonで実行する分には、APIのパスが変わるくらいのはずです。
結果をJSONフォーマットで受け取り、 値に応じてif構文で条件分岐させたりします。
下記はポートチャネルインタフェースの設定値を、 RESTCONFで取得する例です。
import requests url = "https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface/Port-channel-subinterface/Port-channel=1.100" payload={} headers = { 'Accept': 'application/yang-data+json, application/yang-data.errors+json', 'Content-Type': 'application/yang-data+json', 'Authorization': 'Basic <認証トークンを記載>' } response = requests.request("GET", url, headers=headers, data=payload) print(response.text)
結果
{ "Cisco-IOS-XE-native:Port-channel": { "name": "1.100", "encapsulation": { "dot1Q": { "vlan-id": 100 } }, "ip": { "address": { "primary": { "address": "192.168.1.1", "mask": "255.255.255.0" } } } } }
【メリット】 Pythonスクリプトなので何でもできる
【デメリット】機器のAPIドキュメントが少ない場合、試行錯誤が多くなる
Jenkins
直接コマンドを実行しても良いのですが、 Jenkinsを使うとWebブラウザから所定のコマンドを実行できるようです。 今後、学習してみたいと思います。