Arista の Zero Touch Provisioning (ZTP) を試す (Dynamic Provisioning 編) (original : 2014/11/29)
この記事は某所で 2014/11/29
に書いたもののコピーです。
そのため 2017/05/13
時点ではやや古い情報も含まれています。
概要
本項でやること
ZTP を使って、Arista vEOS x2台の初期設定投入をしてみます。
対象機器(Arista)の System MAC アドレスや シリアル番号を事前登録する必要なく、クライアントの LLDP 状態で条件付けして初期設定の動的生成やパラメータ払い出し管理をできるよ、ってところを見ます。
- 方式は Dynamic Provisioning で、対象 vEOS 識別に ztpserver の仕組み(neighbordb)を使う
- 設定ファイルは ztpserver の仕組み(bootstrap と template と definition)を使って動的に生成
- パラメータの払い出しは ztpserver の仕組み(Resource pools)を使う
Static Provisioning(System MAC アドレスやシリアル番号を事前登録する方式)は Arista の Zero Touch Provisioning (ZTP) を試す (Static Provisioning 編) を参照。
Arista EOS 自体については、以下の本をオススメしときますね。
Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS (English Edition)
- 作者: Gary A. Donahue
- 出版社/メーカー: O'Reilly Media
- 発売日: 2012/10/05
- メディア: Kindle版
- この商品を含むブログを見る
Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS
- 作者: Gary A. Donahue
- 出版社/メーカー: O'Reilly Media
- 発売日: 2012/10/27
- メディア: ペーパーバック
- この商品を含むブログを見る
Resource pools とは?
公式の説明 を読むのが手っ取り早いです。
ノード単位で付与するパラメータをプール化しておき、ZTP 実行時に ztpserver が勝手に空いているところを使って利用済みにしてくれる、簡単なリソース管理までやってくれる方式です。
結果概要
ztpserver v1.1.0 で試行した時点では、課題が残りました。いまいちスッキリしてません。詳細は後述します。
- クライアント(vEOS)識別に neighbordb で LLDP 状態マッチングは便利だけど、Arista 側の機能制約起因で実用に少し工夫が必要
- パラメータ払い出しの Resource pools 動作は微妙なので、現時点では実用に結構な工夫が必要
構成図・環境情報
以下の構成でやります。
物理構成は一般的なデータセンタネットワークのそれで、Arista の Multi-Chassis Link Aggregation (MLAG)を使っています。
本項では MLAG の詳細は説明しません & 設定内容もベストプラクティスではないです。
- ztpserver と Arisra vEOS x2 台(spine001/002)はセットアップ済
- Arista vEOS x2 台(leaf001/002)は VirtualBox に突っ込んだだけで何も設定していない
本項の環境は以下の通りです。vEOS は 4 台とも同じスペック。
- VirtualBox と ztpserver v1.1.0 は これ
- vEOS Aboot-veos:Aboot-veos-2.1.0
- vEOS OS:vEOS-4.14.2F
- vEOS CPU:1core
- vEOS Memory:1024MB
vEOS のネットワークアダプタは、構成図の通りです。vEOS 同士の接続は、全部 VirtualBox の intnet で。
事前準備
この辺は軽く…。
ztpserver のインストール~初期設定
これで。
vEOS x4 (spine001/002, leaf001/002)のデプロイ
以下リンクを参考に作っておきます。公式に、各種ハイパーバイザごとの導入手順詳細とかもあります。
vEOS(spine001/002) の設定
必要に応じてネットワークアダプタの追加
VirtualBox の場合は、本構成のように5つ目以降のネットワークアダプタは GUI でできないかも(調べてませんが、バージョン次第?)なので、コマンドラインで足します。
VBoxManage modifyvm vEOS-spine001 --nic5 intnet VBoxManage modifyvm vEOS-spine001 --nicpromisc5 allow-all VBoxManage modifyvm vEOS-spine001 --intnet5 intnet_ar_02 VBoxManage showvminfo "vEOS-spine001" VBoxManage modifyvm vEOS-spine002 --nic5 intnet VBoxManage modifyvm vEOS-spine002 --nicpromisc5 allow-all VBoxManage modifyvm vEOS-spine002 --intnet5 intnet_ar_02 VBoxManage showvminfo "vEOS-spine002"
設定
設定を晒しておきます。
Arista の操作詳細は以下を参考に。MC-LAG の設定内容もマニュアルのサンプルコンフィグほぼそのままです。
spine001
spine001#show run ! Command: show running-config ! device: spine001 (vEOS, EOS-4.14.2F) ! ! boot system flash:/vEOS.swi ! alias ztpprep bash sudo /mnt/flash/ztpprep ! transceiver qsfp default-mode 4x10G ! lldp timer 5 ! hostname spine001 ! spanning-tree mode mstp no spanning-tree vlan 4094 ! no aaa root ! username kotetsu secret 5 $1$H02U1Fnf$JeNAkt5krejSEFwYo7ymu1 ! vlan 4094 ! interface Port-Channel1 description DEV=leaf001 IF=Po1 mlag 1 ! interface Port-Channel2 description DEV=leaf002 IF=Po1 mlag 2 ! interface Port-Channel100 description DEV=spine002 IF=Po100 switchport trunk allowed vlan 4094 switchport mode trunk ! interface Ethernet1 description DEV=leaf001 IF=Eth1 ! interface Ethernet2 description DEV=leaf002 IF=Eth1 ! interface Ethernet3 description DEV=spine002 IF=Eth3 channel-group 100 mode active ! interface Ethernet4 description DEV=spine002 IF=Eth4 channel-group 100 mode active ! interface Management1 ip address 192.168.101.50/24 ! interface Vlan4094 description MC-LAG dedicated PeerLink no autostate ip address 192.0.2.1/30 ! no ip routing ! mlag configuration domain-id DOMAIN_MLAG heartbeat-interval 2500 local-interface Vlan4094 peer-address 192.0.2.2 peer-link Port-Channel100 reload-delay 150 ! management api http-commands no shutdown ! ! end
- spine002
spine002#show run ! Command: show running-config ! device: spine002 (vEOS, EOS-4.14.2F) ! ! boot system flash:/vEOS.swi ! transceiver qsfp default-mode 4x10G ! lldp timer 5 ! hostname spine002 ! spanning-tree mode mstp no spanning-tree vlan 4094 ! no aaa root ! username kotetsu secret 5 $1$HDrZK8m8$A13QQaIqjLdrvik2.3cm9. ! vlan 4094 ! interface Port-Channel1 description DEV=leaf001 IF=Po1 mlag 1 ! interface Port-Channel2 description DEV=leaf002 IF=Po1 mlag 2 ! interface Port-Channel100 description DEV=spine001 IF=Po100 switchport trunk allowed vlan 4094 switchport mode trunk ! interface Ethernet1 description DEV=leaf001 IF=Eth2 ! interface Ethernet2 description DEV=leaf002 IF=Eth2 ! interface Ethernet3 description DEV=spine001 IF=Eth3 channel-group 100 mode active ! interface Ethernet4 description DEV=spine001 IF=Eth4 channel-group 100 mode active ! interface Management1 ip address 192.168.101.51/24 ! interface Vlan4094 description MC-LAG dedicated PeerLink no autostate ip address 192.0.2.2/30 ! no ip routing ! mlag configuration domain-id DOMAIN_MLAG heartbeat-interval 2500 local-interface Vlan4094 peer-address 192.0.2.1 peer-link Port-Channel100 reload-delay 150 ! management api http-commands no shutdown ! ! end
それぞれ、interface Ethernet1-2 設定内に channel-group 設定を入れていません。
これは Dynamic Provisioning で leaf001,002 と LLDP 情報の交換をするために、以下の Arista 仕様を回避するためです。
「Po で LLDP 関係の設定は出来ないから、Eth の方でやってね」って意味だと思っていたのですが、実際に試した限りは Port-Channel 組んでいる物理 IF では LLDP 交換できませんでした。
12.2 LLDP Overview 12.2.4 Guidelines and Limitations
・LLDP is supported only on physical interfaces.
vEOS (leaf001,002) のネットワーク接続
ZTP 実行対象となる 2 台のネットワークアダプタ設定は、事前に VirtualBox でやっておきます。
「配線して電源 ON するだけで初期設定されるよ!」的な ZTP の謳い文句で言うところの配線にあたります。
まあ、仮想環境なんですけどね。
- ネットワークアダプタ1:ホストオンリーネットワーク(ztpserver と同じところ)
- ネットワークアダプタ2:内部ネットワーク(spine001 と同じところ)
- ネットワークアダプタ3:内部ネットワーク(spine002 と同じところ)
そんなわけで、内部的には仮想スイッチ的なものが挟まるので vEOS 間の linkUp/Down 挙動を単純に再現することは難しいです。
ztpserver 設定手順
基本的に 公式の Configuration と 公式の Examples を参照しつつ、必要に応じて ztpserver の source を参照して進めていきます。
neigbordb ファイルの編集 (LLDP 条件と実行処理の紐付け)
Dynamic Provisioning では、ztpserver がクライアントから受け取った LLDP 情報に合わせた処理を実行します。
ここでは、その条件と処理へのリンクの紐付けを /usr/share/ztpserver/neighbordb
に定義します。
patterns: - name: leaf_dynamic definition: def_leaf interfaces: - Ethernet1: spine001:any - Ethernet2: spine002:any
name:
は、本ファイル内で一意であれば何でも良い筈です。
definition:
は、この後作成する definition
のファイル名を記載します。マッチングしたときの処理です。
interfaces:
は、マッチング条件です。leaf の場合、Ethernet1 を spine001 に、Ethernet2 を spine002 に接続するという設計にして、leaf がいくら増えてもこの定義が適用されるように spine 側のポートは any 指定しています。
definition ファイルの作成
Static Provisioning の時とほぼ同様ですが、今回は node 単位ではなくて前述の条件単位なので /usr/share/ztpserver/definitions/
配下に作ります。ファイル名は neighbordb
内で指定した def_leaf
で。
/usr/share/ztpserver/definitions/def_leaf
--- name: def_leaf actions: - action: add_config attributes: url: files/templates/ma1.template variables: ipaddress: allocate('mgmt_subnet') name: "configure ma1" onstart: "Starting to configure ma1" onsuccess: "SUCCESS: ma1 configure" - action: add_config attributes: url: files/templates/system.template variables: hostname: allocate('leaf_hostname') name: "configure global system" onstart: "Starting to add basic system config" onsuccess: "SUCCESS: basic config added" - action: add_config attributes: url: files/templates/login.template name: "configure auth" - action: add_config attributes: url: files/templates/ztpprep.template name: "configure ztpprep alias" - action: add_config attributes: url: files/templates/uplink.template variables: spine_downlink: allocate('spine_downlink') name: "configure uplink" onstart: "Starting to configure uplink" onsuccess: "SUCCESS: uplink configured" - action: copy_file always_execute: true attributes: dst_url: /mnt/flash/ mode: 777 overwrite: if-missing src_url: files/automate/ztpprep name: "automate reload"
variables:
で各 template ファイルに定義されている変数を Resource pools から払い出されるように allocate('<Resource pools ファイル名>')
で書いています。
この後定義する Resource pools のファイル名を指定する必要があります。
各種 template ファイル作成
definition ファイル内で呼び出している各種テンプレートや bash スクリプトを作っておきます。
これは Static Provisioning の時 と同様です。Static/Dynamic の方式問わず共用できます。
/usr/share/ztpserver/files/automate/ztpprep
#!/bin/bash #delete system files and reload the system for ztp rm -rf /mnt/flash/startup-config rm -rf /mnt/flash/*extensions* shutdown -r now
/usr/share/ztpserver/files/templates/ma1.template
interface Management1 ip address $ipaddress no shutdown
/usr/share/ztpserver/files/templates/system.template
hostname $hostname ! ! management api http-commands no shutdown ! lldp timer 5 !
/usr/share/ztpserver/files/templates/system.template
username も password も kotetsu
です。admin
ユーザはデフォルトであります。
username kotetsu secret 5 $1$HDrZK8m8$A13QQaIqjLdrvik2.3cm9.
/usr/share/ztpserver/files/templates/ztpprep.template
alias ztpprep bash sudo /mnt/flash/ztpprep
/usr/share/ztpserver/files/templates/system.template
これは spine001,002 と接続する Uplink の設定です。
interface Ethernet1 description DEV=spine001 IF=Eth$spine_downlink channel-group 1 mode active ! interface Ethernet2 description DEV=spine002 IF=Eth$spine_downlink channel-group 1 mode active ! interface Port-Channel1 description DEV=spine001_002 IF=mlag$spine_downlink
# 最低限の設定を入れたつもりだったが timezone 位は入れておけばよかった…。
Resource pools ファイル作成 (払い出すパラメータ定義)
/usr/share/ztpserver/resources/mgmt_subnet
192.168.101.52/24: null 192.168.101.53/24: null 192.168.101.54/24: null 192.168.101.55/24: null
/usr/share/ztpserver/resources/leaf_hostname
leaf001: null leaf002: null leaf003: null leaf004: null
/usr/share/ztpserver/resources/spine_downlink
1: null 2: null 3: null 4: null
ZTP 動作確認
ztpserver 起動
Dynamic Provisioning でも /usr/share/ztpserver/
配下にノード単位のディレクトリが生成されるので、せやかて sudo します。
$ sudo ztps --debug INFO: [app:115] Logging started for ztpserver INFO: [app:116] Using repository /usr/share/ztpserver DEBUG: [controller:776] server URL: http://192.168.101.16:8080 Starting server on http://192.168.101.16:8080
vEOS 再起動
leaf001,002 のどちらからでもいいですが。
startup-config を消して再起動します。いつものように reload
実行時に色々聞かれるので Save するかは no
を、Confirm は そのまま Enter
で対応します。
# write erase # reload
VirtualBox のコンソールで vEOS 停止~起動を見守ります。ログインプロンプトが出たら、ZTP がはじまります。
ZTP 実行
vEOS 起動後、「startup-config が無いから ZTP はじめるよ」的なメッセージが出て ZTP シーケンスが開始します。画面的には Static Provisioning の時 と代わり映えしないので省略。
ztpserver 側のログを見ると以下の感じ(debug レベルにしているので長い…)
192.168.101.62 - - [27/Nov/2014 01:11:25] "GET /bootstrap HTTP/1.1" 200 35386 192.168.101.62 - - [27/Nov/2014 01:11:27] "GET /bootstrap/config HTTP/1.1" 200 200 DEBUG: [controller:252] POST /nodes HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 339 Content-Type: application/json Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F {"neighbors": {"Ethernet2": [{"device": "spine002", "port": "Ethernet1"}], "Management1": [{"device": "spine002", "port": "Management1"}, {"device": "spine001", "port": "Management1"}], "Ethernet1": [{"device": "spine001", "port": "Ethernet1"}]}, "version": "4.14.2F", "systemmac": "08:00:27:e2:d0:f4", "model": "vEOS", "serialnumber": ""} DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine002:Ethernet1 for interface Ethernet2 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine002:Management1 for interface Management1 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine001:Management1 for interface Management1 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine001:Ethernet1 for interface Ethernet1 DEBUG: [topology:140] 080027e2d0f4: created node object Node(serialnumber=, systemmac=080027e2d0f4, neighbors=OrderedCollection([(u'Ethernet2', [Neighbor(device=u'spine002', interface=u'Ethernet1')]), (u'Management1', [Neighbor(device=u'spine002', interface=u'Management1'), Neighbor(device=u'spine001', interface=u'Management1')]), (u'Ethernet1', [Neighbor(device=u'spine001', interface=u'Ethernet1')])])) DEBUG: [controller:170] 080027e2d0f4: running node_exists DEBUG: [controller:170] 080027e2d0f4: running post_config DEBUG: [controller:170] 080027e2d0f4: running post_node DEBUG: [validators:80] 080027e2d0f4: running NeighbordbValidator.validate DEBUG: [validators:96] 080027e2d0f4: running NeighbordbValidator.validate_patterns DEBUG: [validators:80] 080027e2d0f4: running PatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_attributes for 'leaf_dynamic' WARNING: [validators:171] 080027e2d0f4: PatternValidator warning: 'leaf_dynamic' is missing optional attribute (node) WARNING: [validators:171] 080027e2d0f4: PatternValidator warning: 'leaf_dynamic' is missing optional attribute (variables) DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_definition for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_interfaces for 'leaf_dynamic' DEBUG: [validators:80] 080027e2d0f4: running InterfacePatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running InterfacePatternValidator.validate_interface_pattern DEBUG: [validators:204] 080027e2d0f4: adding interface pattern '{'Ethernet1': 'spine001:any'}' to valid interface patterns DEBUG: [validators:80] 080027e2d0f4: running InterfacePatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running InterfacePatternValidator.validate_interface_pattern DEBUG: [validators:204] 080027e2d0f4: adding interface pattern '{'Ethernet2': 'spine002:any'}' to valid interface patterns DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_name for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_node for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_variables for 'leaf_dynamic' DEBUG: [validators:140] 080027e2d0f4: adding pattern 'leaf_dynamic' ({'definition': 'def_leaf', 'interfaces': [{'Ethernet1': 'spine001:any'}, {'Ethernet2': 'spine002:any'}], 'name': 'leaf_dynamic'}) to valid patterns DEBUG: [validators:96] 080027e2d0f4: running NeighbordbValidator.validate_variables DEBUG: [validators:315] 080027e2d0f4: NeighbordbValidator validation successful DEBUG: [topology:472] 080027e2d0f4: checking pattern 'leaf_dynamic' entries for variable substitution DEBUG: [topology:482] 080027e2d0f4: pattern 'leaf_dynamic' variable substitution complete DEBUG: [topology:369] 080027e2d0f4: pattern 'Pattern(name='leaf_dynamic')' parsed successfully DEBUG: [topology:108] 080027e2d0f4: loaded neighbordb: Neighbordb(variables=0, globals=1, nodes=0) DEBUG: [topology:417] 080027e2d0f4: searching for eligible patterns DEBUG: [topology:426] 080027e2d0f4: all global patterns are eligible DEBUG: [topology:437] 080027e2d0f4: attempting to match pattern leaf_dynamic DEBUG: [topology:559] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match node ("Node(serialnumber=, systemmac=080027e2d0f4, neighbors=OrderedCollection([(u'Ethernet2', [Neighbor(device=u'spine002', interface=u'Ethernet1')]), (u'Management1', [Neighbor(device=u'spine002', interface=u'Management1'), Neighbor(device=u'spine001', interface=u'Management1')]), (u'Ethernet1', [Neighbor(device=u'spine001', interface=u'Ethernet1')])]))") DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Ethernet2([Neighbor(device=u'spine002', interface=u'Ethernet1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet2: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet2(Neighbor(device=u'spine002', interface=u'Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet2: InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet2(Neighbor(device=u'spine002', interface=u'Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:582] 080027e2d0f4: pattern 'leaf_dynamic' - interface pattern match for Ethernet2: InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Management1([Neighbor(device=u'spine002', interface=u'Management1'), Neighbor(device=u'spine001', interface=u'Management1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Management1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Management1(Neighbor(device=u'spine002', interface=u'Management1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Management1(Neighbor(device=u'spine001', interface=u'Management1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:595] 080027e2d0f4: pattern 'leaf_dynamic' - interface Management1 did not match any interface patterns DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Ethernet1([Neighbor(device=u'spine001', interface=u'Ethernet1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet1(Neighbor(device=u'spine001', interface=u'Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:582] 080027e2d0f4: pattern 'leaf_dynamic' - interface pattern match for Ethernet1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:440] 080027e2d0f4: pattern leaf_dynamic matched DEBUG: [controller:373] 080027e2d0f4: 1 pattern(s) in neihgbordb are a good match INFO: [controller:377] 080027e2d0f4: node matched 'leaf_dynamic' pattern in neighbordb INFO: [controller:399] 080027e2d0f4: new /nodes/080027e2d0f4 folder created DEBUG: [controller:170] 080027e2d0f4: running dump_node DEBUG: [controller:170] 080027e2d0f4: running set_location DEBUG: [controller:182] 080027e2d0f4: response to set_location: {'status': 201, 'location': 'nodes/080027e2d0f4'} 192.168.101.62 - - [27/Nov/2014 01:11:49] "POST /nodes HTTP/1.1" 201 0 DEBUG: [controller:470] GET /nodes/080027e2d0f4 HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: 080027e2d0f4 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine002:Ethernet1 for interface Ethernet2 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine002:Management1 for interface Management1 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine001:Management1 for interface Management1 DEBUG: [topology:278] 080027e2d0f4: creating neighbor spine001:Ethernet1 for interface Ethernet1 DEBUG: [topology:140] 080027e2d0f4: created node object Node(serialnumber=None, systemmac=080027e2d0f4, neighbors=OrderedCollection([('Ethernet2', [Neighbor(device='spine002', interface='Ethernet1')]), ('Management1', [Neighbor(device='spine002', interface='Management1'), Neighbor(device='spine001', interface='Management1')]), ('Ethernet1', [Neighbor(device='spine001', interface='Ethernet1')])])) DEBUG: [controller:170] 080027e2d0f4: running get_definition DEBUG: [controller:498] 080027e2d0f4: defintion is nodes/080027e2d0f4/definition ([{'action': 'add_config', 'attributes': {'url': 'files/templates/ma1.template', 'variables': {'ipaddress': "allocate('mgmt_subnet')"}}, 'name': 'configure ma1', 'onstart': 'Starting to configure ma1', 'onsuccess': 'SUCCESS: ma1 configure'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/system.template', 'variables': {'hostname': "allocate('leaf_hostname')"}}, 'name': 'configure global system', 'onstart': 'Starting to add basic system config', 'onsuccess': 'SUCCESS: basic config added'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/login.template'}, 'name': 'configure auth'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/ztpprep.template'}, 'name': 'configure ztpprep alias'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/uplink.template', 'variables': {'spine_downlink': "allocate('spine_downlink')"}}, 'name': 'configure uplink', 'onstart': 'Starting to configure uplink', 'onsuccess': 'SUCCESS: uplink configured'}, {'action': 'copy_file', 'always_execute': True, 'name': 'automate reload', 'attributes': {'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'}}]) DEBUG: [controller:170] 080027e2d0f4: running do_validation DEBUG: [validators:80] 080027e2d0f4: running PatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_attributes for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_definition for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_interfaces for 'leaf_dynamic' DEBUG: [validators:80] 080027e2d0f4: running InterfacePatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running InterfacePatternValidator.validate_interface_pattern DEBUG: [validators:204] 080027e2d0f4: adding interface pattern '{'Ethernet1': 'spine001:any'}' to valid interface patterns DEBUG: [validators:80] 080027e2d0f4: running InterfacePatternValidator.validate DEBUG: [validators:96] 080027e2d0f4: running InterfacePatternValidator.validate_interface_pattern DEBUG: [validators:204] 080027e2d0f4: adding interface pattern '{'Ethernet2': 'spine002:any'}' to valid interface patterns DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_name for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_node for 'leaf_dynamic' DEBUG: [validators:96] 080027e2d0f4: running PatternValidator.validate_variables for 'leaf_dynamic' DEBUG: [validators:315] 080027e2d0f4: PatternValidator validation successful DEBUG: [topology:472] 080027e2d0f4: checking pattern 'leaf_dynamic' entries for variable substitution DEBUG: [topology:482] 080027e2d0f4: pattern 'leaf_dynamic' variable substitution complete DEBUG: [topology:559] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match node ("Node(serialnumber=None, systemmac=080027e2d0f4, neighbors=OrderedCollection([('Ethernet2', [Neighbor(device='spine002', interface='Ethernet1')]), ('Management1', [Neighbor(device='spine002', interface='Management1'), Neighbor(device='spine001', interface='Management1')]), ('Ethernet1', [Neighbor(device='spine001', interface='Ethernet1')])]))") DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Ethernet2([Neighbor(device='spine002', interface='Ethernet1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet2: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet2(Neighbor(device='spine002', interface='Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet2: InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet2(Neighbor(device='spine002', interface='Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:582] 080027e2d0f4: pattern 'leaf_dynamic' - interface pattern match for Ethernet2: InterfacePattern(interface=Ethernet2, remote_device=spine002, remote_interface=any) DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Management1([Neighbor(device='spine002', interface='Management1'), Neighbor(device='spine001', interface='Management1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Management1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Management1(Neighbor(device='spine002', interface='Management1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Management1(Neighbor(device='spine001', interface='Management1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:595] 080027e2d0f4: pattern 'leaf_dynamic' - interface Management1 did not match any interface patterns DEBUG: [topology:569] 080027e2d0f4: pattern 'leaf_dynamic' - attempting to match interface Ethernet1([Neighbor(device='spine001', interface='Ethernet1')]) DEBUG: [topology:575] 080027e2d0f4: pattern 'leaf_dynamic' - checking interface pattern for Ethernet1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:675] 080027e2d0f4: attempting to match Ethernet1(Neighbor(device='spine001', interface='Ethernet1')) against interface pattern InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [topology:582] 080027e2d0f4: pattern 'leaf_dynamic' - interface pattern match for Ethernet1: InterfacePattern(interface=Ethernet1, remote_device=spine001, remote_interface=any) DEBUG: [controller:529] 080027e2d0f4: node passed pattern validation (nodes/080027e2d0f4/pattern) DEBUG: [controller:170] 080027e2d0f4: running get_startup_config DEBUG: [controller:549] 080027e2d0f4: no startup-config nodes/080027e2d0f4/startup-config DEBUG: [controller:170] 080027e2d0f4: running do_actions DEBUG: [controller:567] 080027e2d0f4: action configure ma1 included in definition DEBUG: [controller:567] 080027e2d0f4: action configure global system included in definition DEBUG: [controller:567] 080027e2d0f4: action configure auth included in definition DEBUG: [controller:567] 080027e2d0f4: action configure ztpprep alias included in definition DEBUG: [controller:567] 080027e2d0f4: action configure uplink included in definition DEBUG: [controller:562] 080027e2d0f4: always_execute action automate reload included in definition DEBUG: [controller:170] 080027e2d0f4: running get_attributes WARNING: [controller:589] 080027e2d0f4: no node specific attributes file DEBUG: [controller:170] 080027e2d0f4: running do_substitution DEBUG: [controller:609] 080027e2d0f4: processing action configure ma1 (variable substitution) DEBUG: [controller:609] 080027e2d0f4: processing action configure global system (variable substitution) DEBUG: [controller:609] 080027e2d0f4: processing action configure auth (variable substitution) DEBUG: [controller:609] 080027e2d0f4: processing action configure ztpprep alias (variable substitution) DEBUG: [controller:609] 080027e2d0f4: processing action configure uplink (variable substitution) DEBUG: [controller:609] 080027e2d0f4: processing action automate reload (variable substitution) DEBUG: [controller:170] 080027e2d0f4: running do_resources DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/ma1.template', 'variables': {'ipaddress': "allocate('mgmt_subnet')"}}) DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'ipaddress': "allocate('mgmt_subnet')"}) DEBUG: [resources:76] 080027e2d0f4: allocating resources DEBUG: [resources:108] 080027e2d0f4: looking up resource in 'mgmt_subnet' DEBUG: [resources:90] 080027e2d0f4: allocated 'mgmt_subnet':'192.168.101.54/24' DEBUG: [topology:171] 080027e2d0f4: resources: {'ipaddress': '192.168.101.54/24'} DEBUG: [topology:171] 080027e2d0f4: resources: {'url': 'files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.54/24'}} DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/system.template', 'variables': {'hostname': "allocate('leaf_hostname')"}}) DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'hostname': "allocate('leaf_hostname')"}) DEBUG: [resources:76] 080027e2d0f4: allocating resources DEBUG: [resources:108] 080027e2d0f4: looking up resource in 'leaf_hostname' DEBUG: [resources:90] 080027e2d0f4: allocated 'leaf_hostname':'leaf004' DEBUG: [topology:171] 080027e2d0f4: resources: {'hostname': 'leaf004'} DEBUG: [topology:171] 080027e2d0f4: resources: {'url': 'files/templates/system.template', 'variables': {'hostname': 'leaf004'}} DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/login.template'}) DEBUG: [topology:171] 080027e2d0f4: resources: {'url': 'files/templates/login.template'} DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/ztpprep.template'}) DEBUG: [topology:171] 080027e2d0f4: resources: {'url': 'files/templates/ztpprep.template'} DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/uplink.template', 'variables': {'spine_downlink': "allocate('spine_downlink')"}}) DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'spine_downlink': "allocate('spine_downlink')"}) DEBUG: [resources:76] 080027e2d0f4: allocating resources DEBUG: [resources:108] 080027e2d0f4: looking up resource in 'spine_downlink' DEBUG: [resources:90] 080027e2d0f4: allocated 'spine_downlink':'Eth4' DEBUG: [topology:171] 080027e2d0f4: resources: {'spine_downlink': 'Eth4'} DEBUG: [topology:171] 080027e2d0f4: resources: {'url': 'files/templates/uplink.template', 'variables': {'spine_downlink': 'Eth4'}} DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'}) DEBUG: [topology:171] 080027e2d0f4: resources: {'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'} DEBUG: [controller:170] 080027e2d0f4: running finalize_response DEBUG: [controller:182] 080027e2d0f4: response to finalize_response: {'body': {'name': 'def_leaf', 'actions': [{'action': 'add_config', 'attributes': {'url': 'files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.54/24'}}, 'name': 'configure ma1', 'onstart': 'Starting to configure ma1', 'onsuccess': 'SUCCESS: ma1 configure'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/system.template', 'variables': {'hostname': 'leaf004'}}, 'name': 'configure global system', 'onstart': 'Starting to add basic system config', 'onsuccess': 'SUCCESS: basic config added'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/login.template'}, 'name': 'configure auth'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/ztpprep.template'}, 'name': 'configure ztpprep alias'}, {'action': 'add_config', 'attributes': {'url': 'files/templates/uplink.template', 'variables': {'spine_downlink': 'Eth4'}}, 'name': 'configure uplink', 'onstart': 'Starting to configure uplink', 'onsuccess': 'SUCCESS: uplink configured'}, {'action': 'copy_file', 'always_execute': True, 'name': 'automate reload', 'attributes': {'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'}}]}, 'status': 200, 'content_type': 'application/json'} 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /nodes/080027e2d0f4 HTTP/1.1" 200 1181 DEBUG: [controller:143] GET /actions/add_config HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: add_config 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /actions/add_config HTTP/1.1" 200 3249 DEBUG: [controller:120] GET /files/templates/ma1.template HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: templates/ma1.template 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/templates/ma1.template HTTP/1.1" 200 60 DEBUG: [controller:120] GET /files/templates/system.template HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: templates/system.template 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/templates/system.template HTTP/1.1" 200 84 DEBUG: [controller:120] GET /files/templates/login.template HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: templates/login.template 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/templates/login.template HTTP/1.1" 200 61 DEBUG: [controller:120] GET /files/templates/ztpprep.template HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: templates/ztpprep.template 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/templates/ztpprep.template HTTP/1.1" 200 43 DEBUG: [controller:120] GET /files/templates/uplink.template HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: templates/uplink.template 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/templates/uplink.template HTTP/1.1" 200 279 DEBUG: [controller:143] GET /actions/copy_file HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: copy_file 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /actions/copy_file HTTP/1.1" 200 5589 DEBUG: [controller:120] GET /files/automate/ztpprep HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, compress Content-Length: 4 Content-Type: text/html Host: 192.168.101.16:8080 User-Agent: python-requests/1.2.3 CPython/2.7.0 Linux/3.4.43.Ar-2083164.4142F Resource: automate/ztpprep 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /files/automate/ztpprep HTTP/1.1" 200 144 192.168.101.62 - - [27/Nov/2014 01:11:49] "GET /meta/files/automate/ztpprep HTTP/1.1" 200 65
実行後の状態確認
Resource pools
実行を見守っている時点で「え?」と思ったのですが、Resource pools の払い出しを見ると以下のようになっています。
$ cat /usr/share/ztpserver/resources/mgmt_subnet 192.168.101.52/24: null 192.168.101.53/24: null 192.168.101.54/24: 080027e2d0f4 192.168.101.55/24: null $ cat /usr/share/ztpserver/resources/leaf_hostname leaf001: null leaf002: null leaf003: null leaf004: 080027e2d0f4 $ cat /usr/share/ztpserver/resources/spine_downlink 1: null 2: null 3: null 4: 080027e2d0f4
別に上から順に使われていくわけではないようです。
これについては後述します。
nodes ディレクトリ
/usr/share/ztpserver/nodes/
配下には、Static Provisioning の時には手動生成した「ノード単位のディレクトリ・ファイル」が自動生成されています。
definition
ファイルは、先の手順で生成した /usr/share/ztpserver/definitions/def_leaf
が、neighbordb
マッチングの結果、ほぼそのまま持ってこられています。
$ ls -alh /usr/share/ztpserver/nodes/ total 20K drwxr-xr-x 5 root root 4.0K Nov 29 17:05 . drwxr-xr-x 8 root root 4.0K Nov 27 00:20 .. drwxr-xr-x 2 root root 4.0K Nov 24 02:12 080027316065 drwxr-xr-x 2 root root 4.0K Nov 29 16:35 080027be304d drwxr-xr-x 2 root root 4.0K Nov 29 17:05 080027e2d0f4 $ ls -alh /usr/share/ztpserver/nodes/080027e2d0f4/ total 20K drwxr-xr-x 2 root root 4.0K Nov 29 17:05 . drwxr-xr-x 5 root root 4.0K Nov 29 17:05 .. -rw-r--r-- 1 root root 1.2K Nov 29 17:05 definition -rw-r--r-- 1 root root 314 Nov 29 17:05 .node -rw-r--r-- 1 root root 129 Nov 29 17:05 pattern $ cat /usr/share/ztpserver/nodes/080027e2d0f4/pattern definition: def_leaf interfaces: - Ethernet1: spine001:any - Ethernet2: spine002:any name: leaf_dynamic node: null variables: {} $ cat /usr/share/ztpserver/nodes/080027e2d0f4/.node {"neighbors": {"Ethernet2": [{"device": "spine002", "port": "Ethernet1"}], "Management1": [{"device": "spine001", "port": "Management1"}, {"device": "spine002", "port": "Management1"}], "Ethernet1": [{"device": "spine001", "port": "Ethernet1"}]}, "model": "vEOS", "version": "4.14.2F", "systemmac": "080027e2d0f4"}
Resource pools 動作調査
Resource pools の払い出し判定ってどうなってるの?っていうところを見てみます。
公式の説明 を見ると、空いている最初の値をとってくるように見えるので…。
開発状況
Static Provisioning の時には既知 issue で若干嵌ったので、とりあえず github issue 見ておきます。
ザッと眺めたところ、この機能はまだテストがちゃんとされていないようです。 2014/11/29 時点では v1.2.0 (2014/12 release 予定)を milestone としているようですが、develop branch を見ても特にテストコードは入ってません。
ソースコード
# python 詳しくないので、間違えたこと言ってたらスミマセン
以下が Resource pools の allocate しているログです。
DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'url': 'files/templates/ma1.template', 'variables': {'ipaddress': "allocate('mgmt_subnet')"}}) DEBUG: [topology:147] 080027e2d0f4: computing resources (attr={'ipaddress': "allocate('mgmt_subnet')"}) DEBUG: [resources:76] 080027e2d0f4: allocating resources DEBUG: [resources:108] 080027e2d0f4: looking up resource in 'mgmt_subnet' DEBUG: [resources:90] 080027e2d0f4: allocated 'mgmt_subnet':'192.168.101.54/24'
ソースコードとしては、端的には ここ で、関係するのは以下あたり。
動作としては…
- 対象の Resouce pools ファイルを
/usr/share/ztpserver/resources/
から YAML としてload
(PyYAML) - 上記 YAML の中身を、空の
dict
型 object にkey
,value
格納 (value
が null とか空ならNone
) - 上記の
dict
を走査、value
がNone
ならこいつを割り当てるリソースとしてkey
を取り出す
ってことで、明らかにおかしいところは見つからないけど… dict
型は順序が保証されるものではないから…とか?
OrderedDict
なんていう順序保証型の辞書型もあるらしいけど。
ちなみに、事前に /usr/share/ztpserver/resources/
配下の定義ファイルにコメント入れたり、順序をぐちゃぐちゃにしても、コメントは消されて、ソートされて上書きされます。(dict
をファイルに吐き出している)
一応 MLAG 構成完成させる
仕方ないので、インチキしてとりあえず想定するパラメータが付与されるように leaf001,002 を作りました…。
具体的には /usr/share/ztpserver/resources/
配下の Resource pools 設定ファイルを弄って、付与したいパラメータだけを書いた状態で 1 台ずつ ZTP…。
気持ち悪さを残したまま、 MLAG 設定を作り上げておきます。 以下設定を spine001,002 に追加。
interface Ethernet1 channel-group 1 mode active interface Ethernet2 channel-group 2 mode active
Port-channel を組んだことで、しばし待つと LLDP neighbor から消えてしまいます…。
# Ma1 は VirtualBox 環境なので、同一セグメントの vEOS 間で全部拾ってしまっています。
spine002#show lldp neighbors Last table change time : 0:00:07 ago Number of table inserts : 27 Number of table deletes : 22 Number of table drops : 0 Number of table age-outs : 22 Port Neighbor Device ID Neighbor Port ID TTL Et1 leaf001 Ethernet2 120 Et2 leaf002 Ethernet2 120 Ma1 spine001 Management1 120 Ma1 leaf001 Management1 120 Ma1 leaf002 Management1 120 spine002#show lldp neighbors Last table change time : 0:29:36 ago Number of table inserts : 27 Number of table deletes : 24 Number of table drops : 0 Number of table age-outs : 24 Port Neighbor Device ID Neighbor Port ID TTL Ma1 spine001 Management1 120 Ma1 leaf001 Management1 120 Ma1 leaf002 Management1 120
おわり
課題
以下あたりが課題ですかね。適当にやってるところがあるので、間違えてたら指摘下さい。
- ZTP 関係なく、Port-Channel のメンバ物理 IF では LLDP が動作しないってのはちょっと…。(その後のバージョンや物理箱ではそういう制約は踏まなかったので、この環境固有の問題)
- Resource pools 周りは、巧く動かせなかったので…。現時点では明確に何とも言えないので、暇があったらもう少し追ってみようかと。とりあえず直近 v1.2.0 が出るので、見てますか?
- Resource pools が想定通り動いても、今度は起動順で払い出しを制御しないといけない、とかそもそもどのラックのどのスイッチをどのホスト名にするかを気にしないようにする、とか本番環境で実用する場合には運用を熟考しないといけなさそうです。
neighbordb
の条件付けで正規表現とか、もっと柔軟な定義が使えるようになるようです。そうなると、definition
でのvariables
のところも柔軟にできると結構実用的になりそうです。(e.x. LLDP で送ってきた spine 側のポート番号を使ってhostname: leaf&("%03d", spine_port)
的な書き方)。write erase
からのreload
は必要なので、Zero Touch ではないすね。箱だと出荷時点で startup-config なかったりするのでしょうか。
雑感
- ZTP なんて昔から Cisco がやってるじゃん、と思ってましたが、結構進化していたみたいです。ごめんなさい。
- ztpserver の issue を見ていると、enhance も結構計画されていて期待できそうです。
- Arista は「LLDP 情報を基に port description を更新!」なんてのも出しているし、サーバでもどんどん LLDP 動かそう。
- こういうのを巧く使いこなすには、統一されたネットワーク設計が必要でしょう。それがあって最大限に使いこなすと、ネットワーク屋さんがいなくてもどんどんサーバ増やしていける世界になるんじゃないでしょうか。ていうか、なれ。