IOS XE の「一度消さないと変更できない設定」を Atomic Config Replace で一発適用する
IOS XE の設定変更では、単純に差分のコンフィグを流し込むだけでは変更できない設定があります。
たとえば、既存の設定を一度削除してから入れ直さないと反映できないものです。
-
policy-map type inspectの途中にclass type inspectを追加したい -
ip slaのicmp-echoのsource-interfaceを変更したい
こうした設定は、従来の CLI ベースの投入だと手順が複数段階になりがちです。
今回は Atomic Config Replace (ACR) を活用して、IOS XE の部分的な設定差分を安全に適用するサンプルを紹介します。
困っていたこと
たとえば、次のような policy-map があるとします。
policy-map type inspect PM_ZBF_SELF_TO_INTERNET
class type inspect CM_ZBF_COMMON_ICMP_ERROR
pass
class type inspect CM_ZBF_COMMON_DMVPN
pass
class type inspect CM_ZBF_SELF_TO_INTERNET_DHCP
pass
class type inspect CM_ZBF_COMMON_ALL_L4
inspect
class class-default
drop
既存の class type inspect の間に別の class-map を追加したい場合、設定の並び順や親子関係の都合で、単純な追加コマンドだけでは期待どおりにならないことがあります。
また、今回扱う ip sla でも同じような問題があります。
変更前の設定は次のとおりです。
ip sla 1
icmp-echo 8.8.8.8 source-interface TenGigabitEthernet0/0/8.2068
ip sla schedule 1 life forever start-time now
ip sla 2
icmp-echo 8.8.4.4 source-interface TenGigabitEthernet0/0/8.2068
ip sla schedule 2 life forever start-time now
この source-interface を Loopback1 に変更したい場合、従来は次のような手順が必要でした。
-
ip sla scheduleを無効化する -
ip slaの設定を書き換える -
ip sla scheduleを再度有効化する
つまり、最終的にほしい設定は小さな差分なのに、実際の投入手順は状態依存のオペレーションになります。
Atomic Config Replace でやりたいこと
今回やりたいことは、次の candidate config を IOS XE に渡して、対象範囲だけを置き換えることです。
ip sla 1
icmp-echo 8.8.8.8 source-interface Loopback1
ip sla schedule 1 life forever start-time now
ip sla 2
icmp-echo 8.8.4.4 source-interface Loopback1
ip sla schedule 2 life forever start-time now
下記は私お手製のツールで、現在の running config と candidate config を並べて確認し、Selective replace として差分を作成しています。
差分としては、以下のように source-interface が TenGigabitEthernet0/0/8.2068 から Loopback1 に変わります。
ip sla 1
- icmp-echo 8.8.8.8 source-interface TenGigabitEthernet0/0/8.2068
+ icmp-echo 8.8.8.8 source-interface Loopback1
ip sla schedule 1 life forever start-time now
ip sla 2
- icmp-echo 8.8.4.4 source-interface TenGigabitEthernet0/0/8.2068
+ icmp-echo 8.8.4.4 source-interface Loopback1
ip sla schedule 2 life forever start-time now
適用結果は次のようになりました。
cr1: selective-replace / success / 2047 ms
Configuration applied successfully
変更後の確認
変更後の running config では、ip sla 1 と ip sla 2 の source interface が Loopback1 になっています。
cr1#show run | section ip sla
track 1 ip sla 1 state
track 2 ip sla 2 state
ip sla 1
icmp-echo 8.8.8.8 source-interface Loopback1
ip sla schedule 1 life forever start-time now
ip sla 2
icmp-echo 8.8.4.4 source-interface Loopback1
ip sla schedule 2 life forever start-time now
ip sla 100
http raw http://ipoe-static.ocn.ad.jp
http-raw-request
GET /nic/update?hostname=<redacted> HTTP/1.1\r\nHost: ipoe-static.ocn.ad.jp\r\nAuthorization: Basic <redacted>\r\nConnection: close\r\n\r\n
exit
frequency 3600
ip sla schedule 100 life forever start-time now
show ip sla summary でも、ip sla 1 と ip sla 2 が active で、Return Code が OK になっています。
cr1#show ip sla summary
IPSLAs Latest Operation Summary
Codes: * active, ^ inactive, ~ pending
All Stats are in milliseconds. Stats with u are in microseconds
ID Type Destination Stats Return Last
Code Run
-----------------------------------------------------------------------
*1 icmp-echo 8.8.8.8 RTT=4 OK 27 seconds ago
*2 icmp-echo 8.8.4.4 RTT=4 OK 27 seconds ago
*100 http 2001:380:0:D::108 RTT=142 OK 58 minutes ago
さらに show ip sla configuration 1 でも、対象アドレスと source interface が 8.8.8.8/Loopback1 になっていることを確認できます。
cr1#show ip sla configuration 1
IP SLAs Infrastructure Engine-III
Entry number: 1
Owner:
Tag:
Operation timeout (milliseconds): 5000
Type of operation to perform: icmp-echo
Target address/Source interface: 8.8.8.8/Loopback1
Type Of Service parameter: 0x0
Request size (ARR data portion): 28
Data pattern: 0xABCDABCD
Verify data: No
Vrf Name:
Do not fragment: No
Schedule:
Operation frequency (seconds): 60
Next Scheduled Start Time: Start Time already passed
Group Scheduled : FALSE
Randomly Scheduled : FALSE
Life (seconds): Forever
Entry Ageout (seconds): never
Recurring (Starting Everyday): FALSE
Status of entry (SNMP RowStatus): Active
Threshold (milliseconds): 5000
selective replace の最小サンプル
今回作ったツールでは、内部的には IOS XE の Cisco-IOS-XE-cli-rpc を NETCONF 経由で呼び出しています。
実際の run のコードは公開しない予定なので、ここでは selective replace の要点だけに絞ったサンプルを載せます。
必要な Python ライブラリは ncclient です。
pip install ncclient
candidate config を config-ios-cli-trans RPC の <clis> に入れ、<operation> に selective-replace を指定します。
from html import escape
from ncclient import manager
from ncclient.xml_ import to_ele
HOST = "192.0.2.10"
USERNAME = "admin"
PASSWORD = "password"
def config_replace_rpc(config: str, operation: str = "selective-replace"):
return to_ele(
'<config-ios-cli-trans xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc">'
f"<clis>{escape(config, quote=False)}</clis>"
f"<operation>{operation}</operation>"
"</config-ios-cli-trans>"
)
candidate_config = """\
ip sla 1
icmp-echo 8.8.8.8 source-interface Loopback1
ip sla schedule 1 life forever start-time now
ip sla 2
icmp-echo 8.8.4.4 source-interface Loopback1
ip sla schedule 2 life forever start-time now
"""
with manager.connect(
host=HOST,
port=830,
username=USERNAME,
password=PASSWORD,
hostkey_verify=False,
allow_agent=False,
look_for_keys=False,
device_params={"name": "iosxe"},
) as session:
reply = session.dispatch(config_replace_rpc(candidate_config))
print(reply.xml)
ポイントは、candidate config に configure terminal や end を含めないことです。
この RPC に渡すのは、IOS XE に投入したい CLI 形式の設定本文だけです。
これも重要なポイントで、NetconfでXMLで渡すことができれば問題はないのですが、現実問題自分が必要とするCLI設定をXMLへの変換を即時におこなうことは難しいのかと思います。
Cisco-IOS-XE-cli-rpcであればCLIで渡すことができるわけです。
現在設定を CLI 形式で取得する
差分確認用には、同じ Cisco-IOS-XE-cli-rpc の get-modelled-config-clis を使って running config を CLI 形式で取得できます。
from ncclient import manager
from ncclient.xml_ import to_ele
def get_modelled_config_rpc():
return to_ele(
'<get-modelled-config-clis xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc"/>'
)
with manager.connect(
host=HOST,
port=830,
username=USERNAME,
password=PASSWORD,
hostkey_verify=False,
allow_agent=False,
look_for_keys=False,
device_params={"name": "iosxe"},
) as session:
reply = session.dispatch(get_modelled_config_rpc())
print(reply.xml)
取得した XML から text を取り出して、candidate config と比較すれば、適用前に差分を確認できます。
selective replace 用の diff
selective replace の場合、candidate config に書いたトップレベルの設定だけを running config から抜き出して比較すると、差分が読みやすくなります。
たとえば candidate config が ip sla 1 と ip sla 2 だけなら、running config 側も ip sla 1 と ip sla 2 の section だけに絞ります。
import difflib
def top_level_config_keys(config: str) -> list[str]:
keys = []
for line in config.splitlines():
if not line or line[0].isspace() or line.strip() == "!":
continue
keys.append(line.strip())
return keys
def config_sections(config: str) -> dict[str, list[str]]:
sections = {}
current_key = ""
current_lines = []
for line in config.splitlines():
if line.strip() == "!":
if current_key:
current_lines.append(line)
continue
if line and not line[0].isspace():
if current_key:
sections[current_key] = current_lines
current_key = line.strip()
current_lines = [line]
continue
if current_key:
current_lines.append(line)
if current_key:
sections[current_key] = current_lines
return sections
def selective_current_config(current: str, candidate: str) -> str:
sections = config_sections(current)
lines = []
for key in top_level_config_keys(candidate):
if key in sections:
if lines:
lines.append("!")
lines.extend(sections[key])
return "\n".join(lines)
def build_diff(current: str, candidate: str) -> str:
current_scope = selective_current_config(current, candidate)
return "\n".join(
difflib.unified_diff(
current_scope.splitlines(),
candidate.splitlines(),
fromfile="running:selective-replace",
tofile="candidate:selective-replace",
lineterm="",
)
)
この処理は実際の replace そのものには必須ではありません。
ただ、operator が見る diff としては、全 running config と candidate config を比較するよりもかなり読みやすくなります。
作ったツールの流れ
今回のツールでは、ざっくり次の流れで selective replace を実行しています。
- NetBox から対象デバイスを取得する
- NetBox の tag や管理情報をもとに、対象機器と投入対象の config を選ぶ
- IOS XE から current config を取得する
- candidate config を入力する
- current config と candidate config の差分を作る
- 差分を確認して
APPLYと入力した場合だけ適用する - ACR による selective replace を実行する
- 実行結果と所要時間を画面に表示する
画面では以下の操作を用意しています。
-
Fetch current: 対象機器の現在設定を取得する -
Diff candidate: current config と candidate config の差分を表示する -
Atomic replace: 確認文字列が一致した場合だけ ACR を実行する
これにより、手作業では複数ステップになる設定変更を、candidate config として宣言的に扱えるようになります。
便利だった点
一番大きいメリットは、設定変更を「手順」ではなく「最終的にこうなってほしい状態」として扱えることです。
ip sla のように、いったん schedule を外してから設定を書き換え、最後に schedule を戻す必要がある設定でも、operator は candidate config だけを見ればよくなります。
また、適用前に current config と candidate config の diff を確認できるため、変更対象が想定どおりかどうかを人間が確認しやすくなります。
注意点
今回使った RPC では、画面にも表示しているとおり、一部の機能はサポート対象外です。
Wireless, app-hosting, telemetry features are not supported through this RPC.
そのため、すべての IOS XE 設定をこの方式で扱えるわけではありません。
また、ACR は強力な仕組みなので、適用前の diff 確認、対象範囲の限定、実行ログの保存は必須だと思います。
まとめ
Atomic Config Replace を使うと、IOS XE の「一度消してからでないと変更しづらい設定」を、candidate config ベースで扱えます。
今回の ip sla の例では、source-interface の変更に伴う複数ステップの作業を、selective replace として一度に適用できました。
この手の自動化ではインベントリをどのように管理維持するのかが一つチャレンジだと考えます。
Catalyst Center, Meraki のようなメーカーNMSをつかうのも一つの選択肢です。オープンなツールであるNetBox と組み合わせて対象デバイスや tag を管理し、current config の取得、diff 生成、確認後の apply までをツール化すると、運用作業としてかなり扱いやすくなるのかなと思います。
