2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cisco CML 2.8 Free Tier上にSWを自動配置してAnsibleで設定したい!

Last updated at Posted at 2024-12-02

これを実現します。コードは次を参照してください。

CML2ってノードの初期設定面倒ですよね。以下を自動化します。

  • 1 pythonのvirl2_clientを使ってCML上に外部からアクセス可能なNWノードを自動作成する
    • 1-1 ノードとExternalコネクタ(Bridge)を自動デプロイする
    • 1-2 各ノード間を自動配線する
    • 1-3 Mgmt側I/Fに関する設定を起動時に読み込ませる
  • 2 デプロイしたノードのmgmtアドレスを取得する
  • 3 AnsibleのCisco.iosで簡単なI/F設定とOSPF設定を投入する

この記事ではポイントを絞って説明します。

作りたいネットワーク

image.png

  • rt1 <-> rt2 でOSPF。PC1, PC2のセグメントの経路を交換。
  • 各mgmtはexternal bridge経由で外部(CMLの外)から叩く
  • PC1 -> PC2でpingできればOK

Cisco CMLのインストール

いろいろな人が書いているので割愛します。私が過去に書いた記事は以下です。

lib2_clientを使ったCML上へのノードの自動デプロイ

lib2_clientはCiscoCMLのWebUI上で行えるすべての作業が行えるPythonのライブラリです。

基本的な使い方

# CMLに接続しlabnwを開く
client = ClientLibrary(f"https://192.168.0.100/", "admin", "cisco", ssl_verify=False)
lab = client.create_lab("labnw")
# labにnode1, node2を作り、(新しいI/Fを作って) ノードを接続する
node1 = lab.create_node("RT1", "iol-xe")
node2 = lab.create_node("RT2", "ioll2-xe")
pc1 = lab.create_node("PC1", "alpine") # alpine(linux)を作成
node1.config = "...(node1のconfig)"
node2.config = "...(node2のconfig)"
pc1.config = "..."
lab.connect_two_nodes(node1, node2 ) # この場合、eth0/0.node1 <-> eth0/0.node2が接続される(intfは勝手に作成される)
intf1 = pc1.create_interface() # eth0.pc1が作成される
intf2 = node1.create_interface() # eth0/1.node1が作成される
lab.connect_two_nodes(intf1, intf2) # eth0.pc1 <-> eth0/1.node1が接続される
lab.start()
# ノードが起動しきったら終了する

後述のようにnode.configにbootstrapのscriptを設定しておくことでmgmtアドレスを設定可能です。このアドレスはこのように取得可能です。iosxeでも、alpineでも取得できます。

for node in lab.nodes():
    print(node)
    for interface in node.interfaces():
        print(" ", interface, interface.discovered_ipv4)

特殊なノード

Bridge
# Bridge (external_connectorのconfigにテキストで"System Bridge"と書く)
bridgeNode = lab.create_node("bridge", "external_connector", 0, 0)
bridgeNode.config = "System Bridge"
アンマネージスイッチ
mgmtsw = lab.create_node("mgmtsw", "unmanaged_switch", 0, 500)

Configのポイント

node.configに設定する設定のポイントです。

iol-xeあるいはioll2-xeの場合

ここを参考してください。
sshのkey作成はmod 2048などを与えるとキーボード入力を求められることがありません。

ip domain name cisco
transport input telnet ssh
ip ssh server algorithm authentication password
crypt key gen rsa mod 2048
do write memory

DHCPによるアドレス取得としたい場合は以下の通りとなります。

interface Ethernet 0/0
 ip address dhcp

Alpine設定のポイント

ここを参考にしてください。注意書きに書いた通り、staticにアドレスを指定してもdhcpcが起動します。DHCPのみの付与でよい場合はipコマンド群は不要です。

hostname {hostname}
# ここで設定したUSER, PASSでユーザが作成されます。sudo可能です。
USERNAME={loginUsername}
PASSWORD={loginPassword}
# 以下のようにstaticアドレスを付与できますが、この有無にかかわらず"udhcpcが起動する"ことを注意してください
ip address add {mgmtaddr}/{mgmtsubnet} dev eth0
ip route add default via {mgmtgw}

Nexus設定のポイント

今回のスクリプトには含めていませんがNexusの場合、一番最初のcreate_interface()がmgmtインタフェースです。

インベントリの作成

次の工程でansibleによる自動設定を行いたいのでhostname <-> mgmt ipaddrのマッピングを作ります。

mgmtアドレスをDHCPで設定した場合、デプロイしたノードのmgmtアドレスを知るのは困難に思えますが上記で見た通りCML2ではnodeのinterface.discovered_ipv4に設定されたIPアドレスが格納されています。これを利用して以下のようなアプローチでmgmtを取得できます。

  • アプローチ1: 各nodeのipv4アドレスを見て、あらかじめ設定したmgmtのアドレス空間に含まれていればそれをそのnodeのmgmtアドレスとして扱う
  • アプローチ2: 各ノードの最初のインタフェースがmgmtになるように生成を行いそれをそのnodeのmgmtアドレスとして扱う

今回はこのようにアプローチ2を採用しています。(今回の実装ではeth0あるいはfastether0/0かで判定しています)

for node in lab.nodes():
    nodeName = node._label
    for interface in node.interfaces():
        if "Interface: Ethernet0/0" in str(interface): 
           # この場合、iosxe
        elif "Interface: eth0" in str(interface):
           # この場合、alpine

ansibleのinventoryのテンプレートをこのように作って起き、loadした後にhosts部分を埋めて再度dumpします。

baseYaml = """
all: ...
  children:
    iosxe:
      hosts:
    pc:
      hosts:
...
"""
dat = yaml.safe_load(baseYaml)
dat["all"]["children"]["iosxe"]["hosts"] = {}
dat["all"]["children"]["pc"]["hosts"] = {}
...
         dat["all"]["children"]["pc"]["hosts"][nodeName] = {
              "ansible_host": str(mgmtAddr),
          } # など
...
with open('inventory.yaml','w')as fw:
    yaml.dump(dat, fw, default_flow_style=False, allow_unicode=True)

これにより、このようなinventoryが生成されます。

all:
  children:
    iosxe:
      hosts:
        rt1:
          ansible_host: 192.168.101.222
          ansible_network_os: ios
        rt2:
          ansible_host: 192.168.101.223
          ansible_network_os: ios
    pc:
      hosts:
        pc1:
          ansible_host: 192.168.101.224          

設定する

ここまでくればansibleのcisco.iosモジュールで設定できます。このように各種設定をして以下のように実行しましょう。

ansible-playbook -i inventory.yaml 30_setconfig.yaml

playbookの例
- name: router 1
  hosts: rt1
  gather_facts: no
  tasks:
    - name: rt1-interface-addr
      cisco.ios.ios_l3_interfaces:
        config:
          - name: Ethernet0/1
            ipv4:
              - address: 10.0.0.1/24
        state: merged
    - name: OSPF V2 configuration
      cisco.ios.ios_ospfv2:
        config:
          processes:
            - process_id: 1
              router_id: 10.255.0.1
              log_adjacency_changes:
                set: true
              network:
                - address: 10.0.0.0
                  wildcard_bits: 0.0.0.255
                  area: 0

alpine設定のコツ

alpineにはPythonが入っていないのでbuiltin.rawで流します。しかし、sudo ipで実行すると戻り値がtrueにならないようなので以下のように|| trueを使って強制的にtrueを戻すようにしています。

- hosts: pc1
  gather_facts: no
  tasks:
    - name: link up ip address
      ansible.builtin.raw: sudo ip link set eth1 up || true
    - name : set ip address
      ansible.builtin.raw: sudo ip addr add 10.101.0.2/24 dev eth1 || true
    - name: set route
      ansible.builtin.raw: sudo ip route add 10.0.0.0/8 via 10.101.0.1 || true

動作確認

設定変更と変わりません。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?