【ChatGPT 4oと一緒にAnsible化 Part2】Ciscoルータ(C890)にAnsibleを適用した話

はじめに

わたしは「2台のCisco設定をansible playbookを作成してください」とルータ2台分のコンフィグを貼って依頼し、ChatGPTに作成してもらいました。

私はCiscoでAnsibleを使ったことがなく、完全な素人です。
でもChatGPTにお願いすれば、どこまで簡単にできるだろう?と期待してやってみました。

前回は、ChatGPTにAnsibleのPlaybookを作ってもらったものの、DockerやRocky Linuxの環境構築で大苦戦。なんとか準備が整ったので、今回は実際にCiscoルータ(C890)にAnsibleを適用してみました。

👉 前回の記事はこちら:
【ChatGPTと一緒にAnsible化 Part1】Ciscoルータ(C890)にAnsibleを作成しようと思ったけど、Docker Rockyコンテナで格闘した話


構成

実行環境のバージョン

$ ansible --version
ansible [core 2.16.2]
  config file = /etc/ansible/ansible.cfg
  python version = 3.12.0
  jinja version = 3.1.3
  yaml version = 6.0.1
  • ルータ:Cisco C890シリーズ 2台(rt01, rt02)
  • IPアドレス:
    • rt01: 10.1.100.40
    • rt02: 10.1.100.46
  • OS:IOS 15.4(3)M6a
  • 接続方法:SSH
  • 制御端末:Docker上のRocky LinuxからAnsible実行
  • RT01とRT02にVlan10のネットワークにいるDocker (Rocky8)からansibleを適用します。

Ansible Playbook(最終版)

ディレクトリ構成(プロジェクトルート)

project_root/
├── inventory/
│   └── hosts
├── playbooks/
│   └── cisco_config.yml

インベントリファイル inventory/hosts

[cisco_routers]
rt01 ansible_host=10.1.100.40
rt02 ansible_host=10.1.100.46

[cisco_routers:vars]

ansible_connection=network_cli ansible_network_os=cisco.ios.ios ansible_user=runsuru ansible_password=cisco123 ansible_become=yes ansible_become_method=enable ansible_become_password=cisco123

ディレクトリ構成(プロジェクトルート)

project_root/
├── inventory/
│   └── hosts
├── playbooks/
│   └── cisco_config.yml

以下がChatGPTと対話しながら作成したPlaybookの最終形です。playbooks/cisco_config.yml

よくみると、変数vars: として  bgp_as_map: など定義しているものの、BGPセクションを見るとベタ打ちでした。今回は敢えて訂正せず、このまま実施をします。 

- name: Configure Cisco routers (rt01 / rt02)
  hosts: cisco_routers
  gather_facts: no
  vars:
    loopback_ip_map:
      rt01: "1.1.1.1"
      rt02: "2.2.2.2"
    gig0_ip_map:
      rt01: "192.168.12.1"
      rt02: "192.168.12.2"
    bgp_as_map:
      rt01: "65001"
      rt02: "65002"
    bgp_neighbor_as_map:
      rt01: "65002"
      rt02: "65001"
    bgp_description_map:
      rt01: "BGP peer group to downstream routers"
      rt02: "BGP peer group to upstream routers"
    bgp_password_map:
      rt01: "045802150C2E1D1C5A"
      rt02: "060506324F41584B56"
    bgp_neighbor_ip_map:
      rt01: "192.168.12.2"
      rt02: "192.168.12.1"

  tasks:
    - name: Configure VTY lines 0 to 4
      cisco.ios.ios_config:
       lines:
        - login local
        - rotary 1
        - transport input ssh
       parents: line vty 0 4
       save_when: modified
    - name: Configure VTY lines 5 to 15
      cisco.ios.ios_config:
        lines:
        - login local
        - rotary 1
        - transport input ssh
        parents: line vty 5 15
        save_when: modified

    - name: Set hostname
      cisco.ios.ios_config:
        lines:
          - hostname {{ inventory_hostname }}
        save_when: modified

    - name: Configure loopback interface
      cisco.ios.ios_config:
        lines:
          - interface Loopback0
          - ip address {{ loopback_ip_map[inventory_hostname] }} 255.255.255.255
        parents: interface Loopback0
        match: exact
        replace: line
        save_when: modified

    - name: Configure GigabitEthernet0 interface
      cisco.ios.ios_config:
        lines:
          - interface GigabitEthernet0
          - ip address {{ gig0_ip_map[inventory_hostname] }} 255.255.255.252
          - duplex auto
          - speed auto
          - no shutdown
        parents: interface GigabitEthernet0
        match: exact
        replace: line
        save_when: modified

    - name: Create VLAN10
      cisco.ios.ios_config:
        lines:
           - vlan 10
        save_when: modified

    - name: Configure VLAN10 interface for SSH
      cisco.ios.ios_config:
        lines:
          - description "### for ssh ###"
          - ip address dhcp
        parents: interface Vlan10
        match: exact
        replace: line
        save_when: modified

- name: Configure BGP settings on rt01 and rt02
  hosts: rt01:rt02
  gather_facts: no
  connection: network_cli
  vars:
    ansible_network_os: cisco.ios.ios

  tasks:

    - name: Configure BGP settings on rt01
      when: inventory_hostname == 'rt01'
      cisco.ios.ios_config:
        lines:
          - neighbor PEERS peer-group
          - neighbor PEERS remote-as 65002
          - neighbor PEERS description to-rt02
          - neighbor PEERS password 7 045802150C2E1D1C5A
          - neighbor PEERS timers 5 15
          - neighbor 192.168.12.2 peer-group PEERS
          - network 1.1.1.1 mask 255.255.255.255
          - bgp log-neighbor-changes
        parents: router bgp 65001
        save_when: modified

    - name: Configure BGP settings on rt02
      when: inventory_hostname == 'rt02'
      cisco.ios.ios_config:
        lines:
          - neighbor PEERS peer-group
          - neighbor PEERS remote-as 65001
          - neighbor PEERS description to-rt01
          - neighbor PEERS password 7 045802150C2E1D1C5A
          - neighbor PEERS timers 5 15
          - neighbor 192.168.12.1 peer-group PEERS
          - network 2.2.2.2 mask 255.255.255.255
          - bgp log-neighbor-changes
        parents: router bgp 65002
        save_when: modified

rt02を初期化してから設定投入

シリアルコンソール接続して初期化:

rt02#write erase

*Feb  6 18:27:26.827: %SYS-7-NV_BLOCK_INIT: Initialized the geometry of nvram
rt02#reload

System configuration has been modified. Save? [yes/no]: no
Proceed with reload? [confirm]

----
Would you like to enter the initial configuration dialog? [yes/no]: no

—- 初期コンフィグ例:

! ホスト名(任意)
hostname rt02

! IPアドレス(SSH接続用)
interface Vlan10
 description for ssh
 ip address dhcp
 no shutdown

! スイッチポート側を VLAN10 に接続
interface FastEthernet0
 switchport access vlan 10
 no shutdown

! SSH接続のためのユーザー パスワードはCisco123
username runsuru privilege 15 secret 5 $1$fwYo$rLJGF5x1KnAYVwcfgAArc0

! SSH有効化
ip domain-name example.local
crypto key generate rsa modulus 1024
ip ssh version 2

! VTYラインでSSHログインを許可
line vty 0 4
 login local
 transport input ssh

---


コンフィグを入れたら、sshできるように修正します。

# 初期化(既存のSSHキー情報を削除)
ssh-keygen -R 10.1.100.46

```bash
# SSHでログインして接続確認
ssh runsuru@10.1.100.46

この状態にしておけば、Ansibleでの投入準備が整います。


Playbookの実行ログ

以下は、rt02 に対して実際にPlaybookを実行した結果です。

[runsuru@6a9ba1ecee63 ansible]$ ansible-playbook -i inventory/hosts playbooks/cisco_config.yml -l rt02

PLAY [Configure Cisco routers (rt01 / rt02)] ********************************************************************************************

TASK [Configure VTY lines 0 to 4] *******************************************************************************************************
[WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the
running configuration on device
changed: [rt02]

TASK [Configure VTY lines 5 to 15] ******************************************************************************************************
changed: [rt02]

TASK [Set hostname] *********************************************************************************************************************
ok: [rt02]

TASK [Configure loopback interface] *****************************************************************************************************
changed: [rt02]

TASK [Configure GigabitEthernet0 interface] *********************************************************************************************
changed: [rt02]

TASK [Create VLAN10] ********************************************************************************************************************
changed: [rt02]

TASK [Configure VLAN10 interface for SSH] ***********************************************************************************************
changed: [rt02]

PLAY [Configure BGP settings on rt01 and rt02] ******************************************************************************************

TASK [Configure BGP settings on rt01] ***************************************************************************************************
skipping: [rt02]

TASK [Configure BGP settings on rt02] ***************************************************************************************************
changed: [rt02]

PLAY RECAP ******************************************************************************************************************************
rt02                       : ok=8    changed=7    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

設定が正常に投入され、changed=7 として7つの項目が変更されたことが確認できます。

また、BGPピアも無事に確立されており、経路の広報・受信にも問題はありませんでした。

rt01#show ip bgp summary
Neighbor        V           AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.12.2    4        65002      67      67        3    0    0 00:05:20        1

rt01#show ip bgp neighbors 192.168.12.2 advertised-routes
BGP table version is 3, local router ID is 1.1.1.1

     Network          Next Hop            Metric LocPrf Weight Path
 *>  1.1.1.1/32       0.0.0.0                  0         32768 i

Total number of prefixes 1

rt01#show ip bgp neighbors 192.168.12.2 routes
BGP table version is 3, local router ID is 1.1.1.1

     Network          Next Hop            Metric LocPrf Weight Path
 *>  2.2.2.2/32       192.168.12.2             0             0 65002 i

Total number of prefixes 1
rt01#

ChatGPTとのやりとり(一部抜粋)

最初に提示されたPlaybookは一見正しく見えたものの、いざ実行するといくつかの箇所でエラーが発生しました。

その都度、エラー内容をChatGPTに貼り付けると、すぐに原因と修正案を提示してくれました。

エラー → 修正 → 再実行 を繰り返すことで、最終的に安定したPlaybookが完成しました。


実際のエラーとやりとり例:

TASK [Configure GigabitEthernet0 interface] *********************************************************************************************
fatal: [rt02]: FAILED! => {"changed": false, "module_stderr": "ip address 192.168.12.2 255.255.255.252
ip address 192.168.12.2 255.255.255.252
          ^
% Invalid input detected at '^' marker.

rt02(config)#", "module_stdout": "", "msg": "MODULE FAILURE
See stdout/stderr for the exact error"}

💻 ChatGPT:interface定義とIPアドレス設定が二重になっているか、parents 指定が足りない可能性あり。


TASK [Configure loopback interface] *****************************************************************************************************
fatal: [rt02]: FAILED! => {"changed": false, "module_stderr": "ip address 2.2.2.2 255.255.255.255
ip address 2.2.2.2 255.255.255.255
          ^
% Invalid input detected at '^' marker.

rt02(config)#", "module_stdout": "", "msg": "MODULE FAILURE
See stdout/stderr for the exact error"}

💻 ChatGPT:この場合も parents: interface Loopback0 が不足していた。正しく書く必要あり。


TASK [Configure VLAN10 for SSH] *********************************************************************************************************
fatal: [rt02]: FAILED! => {"changed": false, "module_stderr": "description \"### for ssh
description \"### for ssh
^
% Invalid input detected at '^' marker.

rt02(config-vlan)#", "module_stdout": "", "msg": "MODULE FAILURE
See stdout/stderr for the exact error"}

💻 ChatGPT:VLAN設定において description を入れる場所が不適切だったため、該当セクションの構成を見直すよう提案。



感想

最初からパーフェクトなPlaybookは作れなかったけど、エラーが出てもその出力を貼ってChatGPTに相談すれば、即座に修正案がもらえる。

昔はGoogleでそれっぽいページを開いて、何度も試行錯誤していたけど、今は「会話しながら解決できる」。

これ、ほんとうに作業効率が段違いに違う。

今回は、1つのPlaybookにすべての設定をまとめて適用しましたが、実際の本番環境ではこのままでは利用が難しいと感じました。

なぜならば、拡張性がなく、現実のネットワークにはさまざまな機種や構成が存在し、必要なパラメータも多数あるためです。

Ansibleは、チームで管理することが多く、その場合はGitHubなどのバージョン管理システムを使うのが一般的です。
そうなると、Playbookに平文でパスワードが書かれていると、セキュリティ上の問題になります。

その点を踏まえ、次回はより実践的で再利用性の高いロール分け構成を、ChatGPTと一緒に構築していきたいと思います。


関連リンク

Follow me!

【ChatGPT 4oと一緒にAnsible化 Part2】Ciscoルータ(C890)にAnsibleを適用した話” に対して1件のコメントがあります。

コメントを残す