kakkotetsu

Arista の Zero Touch Provisioning (ZTP) を試す (Static Provisioning 編) (original : 2014/11/24)

この記事は某所で 2014/11/24 に書いたもののコピーです。
そのため 2017/05/13 時点ではやや古い情報も含まれています。

概要

本項でやること

ZTP を使って、Arista vEOS x1台の初期設定投入をしてみます。
Cisco ではちょっとした作りこみが必要だった「設定テンプレートを作成し、対象ノードの MAC アドレスとノードごとの変数(ホスト名や管理IP)の組み合わせを定義して、バラまく startupconfig ファイルを生成」というような動作を、ztpserver の仕組みを使ってより柔軟にできるよ、ってところを見ます。

  • 方式は Static Provisioning で、対象 vEOS の Sysyte MAC アドレスを事前登録
  • 設定ファイルは ztpserver の仕組み(bootstrap と template と definition)を使って動的に生成

Static Provisioning と Dynamic Provisioning とは?

公式の説明 を読むのが手っ取り早いです。
超ザックリ言うと、以下の通り。

  • Static は、事前に ztpserver にクライアント(Arista)の System MAC アドレスかシリアル番号を登録しておく。サーバがクライアントに応じた startup-config か bootstrap スクリプトをバラまくことができる。
  • Dynamic は、ZTP シーケンスの中でクライアントが送ってきた LLDP 情報を基に、サーバ側で条件マッチして bootstrap スクリプトをバラまくことができる。

Dynamic Provisioning こそが ZTP の本領発揮、という感じですが、本項ではまず Static Provisioning を見ておきます。(startup-config 方式は、あまり面白くなさそうなので割愛)

Arista EOS?

Arista EOS については、以下の本をオススメしときますね。

Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS

Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS

構成図・事前準備

Arista の ztpserver インストール~初期設定 で作った ztpserver と、突っ込んだだけで何も設定していない ARISTA vEOS x1 を使います。以下の簡単構成で。(作業時に手元で作ってた絵があまりにお粗末だったので、試しに ShowNet のアイコンを入れてみたが、かえってチグハグに…)

f:id:kakkotetsu:20170513162241j:plain

ARISTA vEOS も 1 台必要なので、以下リンクを参考に作っておきます。公式に、各種ハイパーバイザごとの導入手順詳細とかもあります。

本項の環境は以下の通りです。

  • 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 ネットワークアダプタ1:ホストオンリーネットワーク(ztpserverと同じところ)

設定手順

基本的に 公式の Configuration公式の Examples を参照しつつ、必要に応じて ztpserver の source を参照して進めていきます。

vEOS で System MAC アドレス確認

今回は Static Provisioning を使うので、vEOS 側の System MAC アドレスを拾っておきます。以下の System MAC address に表示されている 12 桁のやつです。仮想機器なので Serial number はなし…。

#show version
Arista vEOS
Hardware version:
Serial number:
System MAC address:  0800.2731.6065

Software image version: 4.14.2F
Architecture:           i386
Internal build version: 4.14.2F-2083164.4142F.1
Internal build ID:      19fe6cb3-1777-40b6-a4e6-53875b30658c

Uptime:                 1 hour and 54 minutes
Total memory:           996152 kB
Free memory:            34112 kB

ztpserver でクライアントノード単位の設定

/usr/share/ztpserver/nodes/ 配下にディレクトリ・ファイル作成していきます。
例によって 公式の Configuration を見ながら。

ディレクトリ作成

以下のように、まずはノードを識別する System MAC アドレスディレクトリを作ります。
この配下に、ノードごとの定義ファイルとか(今回は使いませんが startupconfig ファイルとか)を作ります。

$ sudo mkdir /usr/share/ztpserver/nodes/080027316065

definition ファイル作成

/usr/share/ztpserver/nodes/080027316065/ 配下に definition ファイルを作成します。
見ての通り、クライアント側に実行させたい処理やテンプレート呼び出しと、テンプレートに埋め込むクライアント単位の変数を定義します。

---
name: spine001
actions:
  - name: "configure ma1"
    action: add_config
    attributes:
      url: /files/templates/ma1.template
      variables:
        ipaddress: 192.168.101.50/24
  - name: "configure system"
    action: add_config
    attributes:
      url: /files/templates/system.template
      variables:
        hostname: spine001
  - name: "configure ztpprep alias"
    action: add_config
    attributes:
      url: /files/templates/ztpprep.template
  - name: "automate reload"
    action: copy_file
    always_execute: true
    attributes:
      dst_url: /mnt/flash/
      overwrite: if-missing
      src_url: files/automate/ztpprep
      mode: 777

間抜けな&当然の話ですが、yaml の文法を間違えたりすると巧いこと vEOS に設定が反映されません。自分はそれでミスって実機に "hostname $hostname" なんてのが設定されてしまいました。 ztps 起動時に、文法チェックとかはしないのです。

pattern ファイル作成

/usr/share/ztpserver/nodes/080027316065/ 配下に pattern ファイルを作成します。
これは、ztpserver の Global 設定 /etc/ztpserver/ztpserver.conf 内で disable_topology_validation = True にしている場合には作成する必要ないです。(公式の記載)
ただ、今後 Dynamic Provisioning を使う際に、これを True にしておくと都合が悪いので False のままにしておきます。この場合、pattern ファイルに記入する内容で「クライアントがどんな LLDP 状態を送ってきても、それを無視する」を実現させます。

name: static_node
interfaces:
  - none: none:none

ここで気を付けるのは、公式の記載 そのままに interfaces: 内で any: any:any と書いても、「クライアントの LLDP が動いていない・LLDP で何も情報を得ていない場合」には期待する動作にはならず異常終了してしまうことです。
これについては github の issue に記載されていますが、公式ドキュメントには 2014/11/24 現在反映されていないようです。

また、ソースコードの master / develop ブランチを比較すると、pattern ファイルの必須パラメータなども今後変わっていく気配があります。上記設定はあくまで v1.1.0 版ということで。

ztpserver に各種テンプレートを作成

前述の definition ファイル内で呼び出している各種テンプレートや bash スクリプトを作っておきます。
公式のサンプルを持ってきて、必要に応じてカスタマイズして配置するのが手っ取り早いと思います。

  • /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/ztpprep.template
alias ztpprep bash sudo /mnt/flash/ztpprep

# 超最低限の設定を入れたつもりだったが、login.template でユーザ登録くらいはしておくべきだった…。

ZTP 動作確認

実際に動かして、vEOS 1台が設定されることを確認します。

ztpserver 起動

$ 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 再起動

startup-config を消して再起動します。いつものように reload 実行時に色々聞かれるので Save するかは no を、Confirm は そのまま Enter で対応します。

# write erase
# reload

VirtualBox のコンソールで vEOS 停止~起動を見守ります。ログインプロンプトが出たら、ZTP がはじまります。

ZTP 実行

vEOS 起動後、以下のようなメッセージが出て ZTP シーケンスが開始します。左の窓が ztpserver で、右の窓が vEOS です。 f:id:kakkotetsu:20170513162524j:plain

公式のシーケンス図と見比べながら、見守ります。 f:id:kakkotetsu:20170513162536j:plain

definition ファイルで定義した通りに動作していることをフムフム言いながら見ていると、vEOS の再起動が始まります。 f:id:kakkotetsu:20170513162548j:plain

vEOS 再起動が終わったら、定義した通りの startup-config で起動していることを確認できます。 f:id:kakkotetsu:20170513162601j:plain

ztpserver 側のログを見ると以下の感じ(debug レベルにしているので長い…)

$ 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
192.168.101.61 - - [24/Nov/2014 02:17:00] "GET /bootstrap HTTP/1.1" 200 35386
192.168.101.61 - - [24/Nov/2014 02:17:01] "GET /bootstrap/config HTTP/1.1" 200 200
DEBUG: [controller:252] POST /nodes HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 110
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": {}, "version": "4.14.2F", "systemmac": "08:00:27:31:60:65", "model": "vEOS", "serialnumber": ""}

DEBUG: [topology:140] 080027316065: created node object Node(serialnumber=, systemmac=080027316065, neighbors=OrderedCollection())
DEBUG: [controller:170] 080027316065: running node_exists
DEBUG: [controller:170] 080027316065: running dump_node
DEBUG: [controller:170] 080027316065: running set_location
DEBUG: [controller:182] 080027316065: response to set_location: {'status': 409, 'location': 'nodes/080027316065'}
192.168.101.61 - - [24/Nov/2014 02:17:28] "POST /nodes HTTP/1.1" 409 0
DEBUG: [controller:470] GET /nodes/080027316065 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: 080027316065

DEBUG: [topology:140] 080027316065: created node object Node(serialnumber=None, systemmac=080027316065, neighbors=OrderedCollection())
DEBUG: [controller:170] 080027316065: running get_definition
DEBUG: [controller:498] 080027316065: defintion is nodes/080027316065/definition ([{'action': 'add_config', 'attributes': {'url': '/files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.50/24'}}, 'name': 'configure ma1'}, {'action': 'add_config', 'attributes': {'url': '/files/templates/system.template', 'variables': {'hostname': 'spine001'}}, 'name': 'configure system'}, {'action': 'add_config', 'attributes': {'url': '/files/templates/ztpprep.template'}, 'name': 'configure ztpprep alias'}, {'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] 080027316065: running do_validation
DEBUG: [validators:80] 080027316065: running PatternValidator.validate
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_attributes for 'static_node'
WARNING: [validators:171] 080027316065: PatternValidator warning: 'static_node' is missing optional attribute (definition)
WARNING: [validators:171] 080027316065: PatternValidator warning: 'static_node' is missing optional attribute (variables)
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_definition for 'static_node'
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_interfaces for 'static_node'
DEBUG: [validators:80] 080027316065: running InterfacePatternValidator.validate
DEBUG: [validators:96] 080027316065: running InterfacePatternValidator.validate_interface_pattern 
DEBUG: [validators:204] 080027316065: adding interface pattern '{'none': 'none:none'}' to valid interface patterns
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_name for 'static_node'
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_node for 'static_node'
DEBUG: [validators:96] 080027316065: running PatternValidator.validate_variables for 'static_node'
DEBUG: [validators:315] 080027316065: PatternValidator validation successful
DEBUG: [topology:472] 080027316065: checking pattern 'static_node' entries for variable substitution
DEBUG: [topology:482] 080027316065: pattern 'static_node' variable substitution complete
DEBUG: [topology:559] 080027316065: pattern 'static_node' - attempting to match node ('Node(serialnumber=None, systemmac=080027316065, neighbors=OrderedCollection())')
DEBUG: [controller:529] 080027316065: node passed pattern validation (nodes/080027316065/pattern)
DEBUG: [controller:170] 080027316065: running get_startup_config
DEBUG: [controller:549] 080027316065: no startup-config nodes/080027316065/startup-config
DEBUG: [controller:170] 080027316065: running do_actions
DEBUG: [controller:567] 080027316065: action configure ma1 included in definition
DEBUG: [controller:567] 080027316065: action configure system included in definition
DEBUG: [controller:567] 080027316065: action configure ztpprep alias included in definition
DEBUG: [controller:562] 080027316065: always_execute action automate reload included in definition
DEBUG: [controller:170] 080027316065: running get_attributes
WARNING: [controller:589] 080027316065: no node specific attributes file
DEBUG: [controller:170] 080027316065: running do_substitution
DEBUG: [controller:609] 080027316065: processing action configure ma1 (variable substitution)
DEBUG: [controller:609] 080027316065: processing action configure system (variable substitution)
DEBUG: [controller:609] 080027316065: processing action configure ztpprep alias (variable substitution)
DEBUG: [controller:609] 080027316065: processing action automate reload (variable substitution)
DEBUG: [controller:170] 080027316065: running do_resources
DEBUG: [topology:147] 080027316065: computing resources (attr={'url': '/files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.50/24'}})
DEBUG: [topology:147] 080027316065: computing resources (attr={'ipaddress': '192.168.101.50/24'})
DEBUG: [topology:171] 080027316065: resources: {'ipaddress': '192.168.101.50/24'}
DEBUG: [topology:171] 080027316065: resources: {'url': '/files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.50/24'}}
DEBUG: [topology:147] 080027316065: computing resources (attr={'url': '/files/templates/system.template', 'variables': {'hostname': 'spine001'}})
DEBUG: [topology:147] 080027316065: computing resources (attr={'hostname': 'spine001'})
DEBUG: [topology:171] 080027316065: resources: {'hostname': 'spine001'}
DEBUG: [topology:171] 080027316065: resources: {'url': '/files/templates/system.template', 'variables': {'hostname': 'spine001'}}
DEBUG: [topology:147] 080027316065: computing resources (attr={'url': '/files/templates/ztpprep.template'})
DEBUG: [topology:171] 080027316065: resources: {'url': '/files/templates/ztpprep.template'}
DEBUG: [topology:147] 080027316065: computing resources (attr={'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'})
DEBUG: [topology:171] 080027316065: resources: {'dst_url': '/mnt/flash/', 'src_url': 'files/automate/ztpprep', 'mode': 777, 'overwrite': 'if-missing'}
DEBUG: [controller:170] 080027316065: running finalize_response
DEBUG: [controller:182] 080027316065: response to finalize_response: {'body': {'name': 'spine001', 'actions': [{'action': 'add_config', 'attributes': {'url': '/files/templates/ma1.template', 'variables': {'ipaddress': '192.168.101.50/24'}}, 'name': 'configure ma1'}, {'action': 'add_config', 'attributes': {'url': '/files/templates/system.template', 'variables': {'hostname': 'spine001'}}, 'name': 'configure system'}, {'action': 'add_config', 'attributes': {'url': '/files/templates/ztpprep.template'}, 'name': 'configure ztpprep alias'}, {'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.61 - - [24/Nov/2014 02:17:28] "GET /nodes/080027316065 HTTP/1.1" 200 657
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.61 - - [24/Nov/2014 02:17:28] "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.61 - - [24/Nov/2014 02:17:28] "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.61 - - [24/Nov/2014 02:17:28] "GET /files/templates/system.template HTTP/1.1" 200 84
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.61 - - [24/Nov/2014 02:17:28] "GET /files/templates/ztpprep.template HTTP/1.1" 200 43
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.61 - - [24/Nov/2014 02:17:28] "GET /actions/copy_file HTTP/1.1" 200 5589

あと、nodes ディレクトリ配下に .node ファイルができています。このファイルが残ったまま再実行しても、特にエラーにはならないです。

$ ls -alh /usr/share/ztpserver/nodes/080027316065/
total 20K
drwxr-xr-x 2 root root 4.0K Nov 24 02:12 .
drwxr-xr-x 3 root root 4.0K Nov 23 11:19 ..
-rw-r--r-- 1 root root  665 Nov 24 02:12 definition
-rw-r--r-- 1 root root   85 Nov 24 02:17 .node
-rw-r--r-- 1 root root   69 Nov 24 01:55 pattern

$ cat /usr/share/ztpserver/nodes/080027316065/.node
{"neighbors": {}, "model": "vEOS", "version": "4.14.2F", "systemmac": "080027316065"}

おわり

Cisco でやったことあればフーンという感じではありますが…template や変数埋め込みの仕組みが簡易なのや、bootstrap の仕組みで Arista 側に任意の bash スクリプトを実行させることが出来るのは、嬉しいかもです。

でも個人的に一番嬉しいのは、仮想OSとして各種ハイパーバイザ上で動かせるイメージが配布されていることで、こういうちょっとした機能試験が手元で簡単にできることです。(例えば別メーカで言うと Juniper の Junos を弄る場合 FireFly が使えますが、EX/MX シリーズとは勿論機能が違うし、箱版の SRX よりできることが少ないです。結果、EX を試したい時には EX2200-C とかの小さなファンレス機を探して箱物を買う羽目に…)

まあ、本領発揮は Dynamic Provisioning で!ということで。

Arista の ztpserver インストール~初期設定 (original : 2014/11/24)

この記事は某所で 2014/11/24 に書いたもののコピーです。
そのため 2017/05/13 時点ではやや古い情報も含まれています。

概要

本項の内容

Arista が公開している ztpserver(2014/11/24 時点の release 版である v1.1.0) を Ubuntu に手動でインストールして初期設定します。

ZTP (zero touch provisioning) とは?

ZTP とは PXE ブートみたいなプロビジョニングを Arista のスイッチで実現する方式です。
スイッチを結線して起動すると、DHCP でアドレスを取得して config ファイルや bootstrap を ztpserver から取得して初期設定されます。
スイッチでは旧来 Cisco が "Smart Install" という名前で実現している方法で、Arista の ZTP は旧来の Cisco より出来ることが多いです。
Arista の ZTP 概要(動作フローなど)は公式ドキュメント Overview 章 ZTP Overview を見ると手っ取り早く掴めます。

参考リンク

Arista EOS?

Arista EOS 自体については、以下の本をオススメしときますね。

Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS

Arista Warrior: A Real-World Guide to Understanding Arista Switches and EOS

Arista ZTP に関する必読系

Packer で楽々インストール (本項では使わず)

Arista 公式で Packer も公開されているので、この辺を使うと手っ取り早く作って試せます。ネットワーク構成なども固まってしまうので、必要に応じてカスタマイズして使うのが良いと思います。本項の手動インストールでは参考資料として使いました。

事前準備

ztpserver をインストールするサーバを準備して、Python 2.7 以上をインストールしておきます。
あとは 公式の Requirement を満たせば、何でも良いと思います。
本項の環境は以下の通りです。(ztpserver は GuestOS にインストールしました。)

インストール~初期設定手順

基本的に 公式のInstallation に従いつつ、必要に応じて 公式の Ubuntu12.04 用 ztpserver Packer レシピ を参考にしながら進めます。

必要パッケージインストール

とりあえず Python が 2.7 以上であることを確認します。

$ python --version
Python 2.7.6

必要なパッケージを apt-get で install していきます。面倒なら適当に -y とかつけて下さい。
ZTP v1.1.0 で bootstrap を使う場合、XMPP サーバと接続できないと異常終了してしまいます。これは ZTP 用のサーバとは別に居ても良いのですが、今回は相乗りさせるので ejabberd も入れておきます。

$ sudo apt-get install python-setuptools python-pip python-dev li bxml2-dev libxslt-dev
$ sudo apt-get install git
$ sudo apt-get install libyaml-dev
$ sudo apt-get install isc-dhcp-server
$ sudo apt-get install ejabberd
$ sudo apt-get install libapache2-mod-wsgi

ztpserver インストール

お手軽に pip install で入れるか git clone してコンパイルするか、お好きな方で。

ztpserver インストール (pip 版)

pip で簡単にインストールできます。これで実行したら、次項の git clone 版は実行不要です。
ファイルが /etc//usr/share/ 配下に展開されるので sudo してます。

$ sudo pip install ztpserver

$ ztps -v
ztps version 1.1.0

ztpserver インストール (git clone 版)

pip install では味気ないと思うなら、手動でやることもできます。とはいえ、git clone して setup 叩くだけの簡単なお仕事です。

github から clone します。

$ git clone https://github.com/arista-eosplus/ztpserver.git

$ cd ztpserver/
$ git show
commit de90d377dd4be7f2fe5eccc70c4f3ca11c8fdc9a
Author: Andrei Dvornic <advornic@aristanetworks.com>
Date:   Fri Nov 21 19:53:00 2014 +0000

    fixes #242

diff --git a/client/bootstrap b/client/bootstrap
index 0692900..5512442 100644
--- a/client/bootstrap
+++ b/client/bootstrap
@@ -358,7 +358,8 @@ class Node(object):
             self._disable_copp()
         except jsonrpclib.jsonrpc.ProtocolError as err:
             log('WARNING: unable to disable COPP: %s '
-                '(can be ignored for EOS-4.11.x and older)' % err)
+                '(platform/EOS version might not support this feature)' % 
+                err)
 
         global SYSTEM_ID                    #pylint: disable=W0603
         SYSTEM_ID = \
@@ -397,8 +398,9 @@ class Node(object):
                 'show management api http-commands | grep running')
 
     def _disable_copp(self):
-        # COPP does not apply to vEOS
-        if self.system()['model'] != 'vEOS':
+        # COPP does not apply to vEOS or EOS-4.11.x and earlier
+        if (self.system()['model'] != 'vEOS' and
+            int(self.system()['version'].split('.')[1]) < 12):
             self.api_config_cmds(['control-plane',
                                   'no service-policy input copp-system-policy'])
 

2014/11/24 時点のリリース版を checkout します。

$ git checkout v1.1.0
Note: checking out 'v1.1.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 269e7c2... Merge pull request #199 from arista-eosplus/rtd-docs

$ git show
commit 269e7c264c459c3af13104d28a9fd140cb3be47d
Merge: 53fb159 4731287
Author: Jere Julian <jerearista@users.noreply.github.com>
Date:   Wed Aug 27 19:25:06 2014 -0400

    Merge pull request #199 from arista-eosplus/rtd-docs
    
    Add capability for sphinx at ReadTheDocs to find bootstrap and actions docstrings

$ ls -alh
total 100K
drwxrwxr-x 11 kotetsu kotetsu 4.0K Nov 23 00:42 .
drwxr-xr-x  4 kotetsu kotetsu 4.0K Nov 23 00:40 ..
drwxrwxr-x  2 kotetsu kotetsu 4.0K Nov 23 00:42 actions
drwxrwxr-x  2 kotetsu kotetsu 4.0K Nov 23 00:42 bin
-rw-rw-r--  1 kotetsu kotetsu   20 Nov 23 00:42 CHANGELOG.md
drwxrwxr-x  3 kotetsu kotetsu 4.0K Nov 23 00:42 client
drwxrwxr-x  2 kotetsu kotetsu 4.0K Nov 23 00:42 conf
-rw-rw-r--  1 kotetsu kotetsu 1.8K Nov 23 00:41 CONTRIBUTING.md
drwxrwxr-x  3 kotetsu kotetsu 4.0K Nov 23 00:42 docs
drwxrwxr-x  8 kotetsu kotetsu 4.0K Nov 23 00:42 .git
-rw-rw-r--  1 kotetsu kotetsu  400 Nov 23 00:42 .gitignore
-rw-rw-r--  1 kotetsu kotetsu  300 Nov 23 00:41 INSTALL.md
-rw-rw-r--  1 kotetsu kotetsu 2.3K Nov 23 00:42 LICENSE
-rw-rw-r--  1 kotetsu kotetsu 2.0K Nov 23 00:42 Makefile
-rw-rw-r--  1 kotetsu kotetsu   96 Nov 23 00:42 MANIFEST.in
-rw-rw-r--  1 kotetsu kotetsu 1.1K Nov 23 00:42 .pylintrc
-rw-rw-r--  1 kotetsu kotetsu 2.5K Nov 23 00:41 README.md
-rw-rw-r--  1 kotetsu kotetsu   55 Nov 23 00:41 requirements.txt
-rw-rw-r--  1 kotetsu kotetsu   37 Nov 23 00:41 setup.cfg
-rw-rw-r--  1 kotetsu kotetsu 3.2K Nov 23 00:42 setup.py
drwxrwxr-x  6 kotetsu kotetsu 4.0K Nov 23 00:41 test
-rw-rw-r--  1 kotetsu kotetsu  353 Nov 23 00:41 .travis.yml
drwxrwxr-x  2 kotetsu kotetsu 4.0K Nov 23 00:41 utils
-rw-rw-r--  1 kotetsu kotetsu    6 Nov 23 00:42 VERSION
drwxrwxr-x  2 kotetsu kotetsu 4.0K Nov 23 00:42 ztpserver

build ~ install します。
ファイルが /etc//usr/share/ 配下に展開されるので sudo してます。

$ python setup.py build

$ sudo python setup.py install

$ ztps -v
ztps version 1.1.0

DHCPサーバ (isc-dhcp-server) の設定

/etc/dhcp/dhcpd.conf に環境に合わせた設定を追加しておきます。
domain-name 関係は今回使っていないので、設定しません。(内部 DNS サーバを使って、XMPPサーバや ZTP サーバをドメインで渡す場合には設定して下さい。本項では面倒なのでやっていないだけで、公式 Packer ではそうしています。)

以下例で range に指定しているのは、ZTP の DHCP シーケンス中で一時的に Arista に付与されるアドレスなので、複数機器を同時に多数 ZTP 処理するような場合には広めにとっておきましょう。
option bootfile-name には ZTP サーバのアドレスを入れておきます。ポート番号や path はこのままで OK です。

/etc/dhcp/dhcpd.conf の追加設定例

#Subnet created for VEOS devices
subnet 192.168.101.0 netmask 255.255.255.0 {
 range 192.168.101.61 192.168.101.69;
 option bootfile-name "http://192.168.101.16:8080/bootstrap";
 default-lease-time 86400;
 max-lease-time 86400;
}

サービス起動

$ sudo service isc-dhcp-server start

runlevel は 2345 になっている筈です。/etc/init/isc-dhcp-server.conf 参照。

XMPP サーバ (ejabberd) の設定

bootstrap を使うために必要なので、設定します。多分、startup-config ファイルを配布する方式だけを使う場合には、XMPP サーバは不要だと思います。

まず /etc/ejabberd/ejaejabberd.cfg を編集。
以下設定例で ztpsadmin の部分は、この後登録する ejabberd の admin ユーザ名です。(何でも良いです。)

%% Admin user
{acl, admin, {user, "ztpsadmin", "localhost"}}.

ejabberd の admin ユーザを登録します。以下例では ztpsadmin が設定ファイルにも書いたユーザ名で eosplus はパスワード文字列です。

$ sudo ejabberdctl register ztpsadmin localhost eosplus
User ztpsadmin@localhost successfully registered

サービスを起動します。

$ sudo service ejabberd restart
Restarting jabber server: ejabberd is not running. Starting ejabberd.

$ ejabberd status とか見ても、正常なのか良く分からなかったので、Web ブラウザで動作確認しました。http://<ejabberd 入れたサーバの IP アドレス>:5280/admin にアクセスしてユーザ名に ztpsadmin@localhost を、パスワードに eosplus を入力して、何かオレンジの画面が出たらきっと動いてます。WebUI で設定変更とか出来るようです。

f:id:kakkotetsu:20170513161309j:plain

ztpserver の設定

ZTP の方式によって色々とカスタマイズできる個別設定は別項として、ここでは共通設定を済ませておきます。
例によって 公式の Configuration公式の Examples に従い、必要に応じて 公式 Packer で配布している設定ファイル群 を参考に進めます。

Global configuration file (/etc/ztpserver/ztpserver.conf) 編集

/etc/ztpserver/ztpserver.conf を編集します。
デフォルトの identifier = serialnumberidentifier = systemmac にしています。自分が試した vEOS (公式で配布されている仮想OS)は、ZTP シーケンス時 serialnumber を空で ZTP サーバに送信していたので、identifier = serialnumber だと異常終了してしまったからです。

[default]

# Location of all ztps boostrap process data files
data_root = /usr/share/ztpserver

# UID used in the /nodes structure (either systemmac or serialnumber)
identifier = systemmac

# Server URL to-be-advertised to clients (via POST replies) during the bootstrap process
server_url = http://192.168.101.16:8080

# Enable local logging
logging = True

# Enable console logging
console_logging = True

# Globally disable topology validation in the bootstrap process
disable_topology_validation = False


[server]

# Note: this section only applies to using the standalone server.  If
# running under a WSGI server, these values are ignored

# Interface to which the server will bind to (0:0:0:0 will bind to
# all available IPv4 addresses on the local machine)
interface = 192.168.101.16

# TCP listening port
port = 8080


[ files]
# Path for the files directory (overriding data_root/files)
folder = files
path_prefix = /usr/share/ztpserver

[actions]
# Path for the actions directory (overriding data_root/actions)
folder = actions
path_prefix = /usr/share/ztpserver

[bootstrap]
# Path for the bootstrap directory (overriding data_root/bootstrap)
folder = bootstrap
path_prefix = /usr/share/ztpserver

# Bootstrap filename
filename = bootstrap


[neighbordb]

# Neighbordb filename (file located in data_root)
filename = neighbordb

bootstrap 設定ファイル (/usr/share/ztpserver/bootstrap/bootstrap.conf) 編集

これは bootstrap 方式で ZTP する場合にだけ使われるので、共通設定とはいえないかもですが。

/usr/share/ztpserver/bootstrap/bootstrap.conf を編集します。xmpp のところには、ejaejabberd で設定した usernamepassword を書きます。domain は本項の環境では使わないので、IP アドレスを。

logging:
   - destination: file:/tmp/ztps-log
     level: DEBUG

xmpp:
   username: ztpsadmin
   password: eosplus
   domain: 192.168.101.16
   msg_type : debug
   rooms:
     - xmpproom

ztpserver 起動

以下のように起動します。ZTP の方式によってはシーケンス中に /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

おわり

インストールと初期設定だけなので、特筆すべき点はないです。