Provision a Static Node¶
I want to provision my switch based upon its System ID (System MAC Address or Serial Number).
Log into your (v)EOS node to get its System ID. If it’s in ZTP Mode, just log in with username admin:
switch-name> show version
Copy down the System ID (System MAC Address or Serial Number).
Let’s create a node directory for this device:
# Go to your data_root - by default it's /usr/share/ztpserver admin@ztpserver:~# cd /usr/share/ztpserver # Move to the nodes directory, where all node information is stored admin@ztpserver:~# cd nodes # Create a directory using the MAC Address you found earlier admin@ztpserver:~# mkdir <SYSTEM_ID>
A node is considered to be statically provisioned when a directory with its System ID is already located in the nodes/ directory.
Note that the System ID can be the node’s System MAC Address or its Serial Number.
Just adding this directory is not enough to provision the node. The remaining recipes will finish off the task. To successfully provision a node statically, you will need to create:
- pattern file - if Topology Validation is enabled
- definition - if you choose to apply other actions during provisioning
and place them in [data_root]/nodes/<SYSTEM_ID>.
Confirm your ZTPServer Configuration will identify a node based upon the desired System ID by checking /etc/ztpserver/ztpserver.conf and check the value of identifier
I want the node to receive a startup-config during provisioning.
Create a file named startup-config in [data_root]/nodes/<SYSTEM_ID>/.
Place the desired configuration into the startup-config. Here’s an example. Please change values where you see fit:
! hostname test-node-1 ip name-server vrf default <DNS-SERVER-IP> ! ntp server <NTP-SERVER-IP> ! username admin privilege 15 role network-admin secret admin ! interface Management1 ip address <MGMT-IP-ADDRESS>/<SUBNET> ! ip access-list open 10 permit ip any any ! ip route 0.0.0.0/0 <DEFAULT-GW> ! ip routing ! management api http-commands no shutdown ! banner login Welcome to $(hostname)! This switch has been provisioned using the ZTPServer from Arista Networks Docs: http://ztpserver.readthedocs.org/ Source Code: https://github.com/arista-eosplus/ztpserver EOF ! end
I have created a static node directory and Topology Validation is enabled, so I would like to make sure everything is wired up correctly before provisioning a node.
YAML syntax can be a pain sometimes. The indentation is done with spaces and not tabs.
Create a file named pattern in [data_root]/nodes/<SYSTEM_ID>/ and define the LLDP associations.
Example 1: Match any neighbor
This pattern essentially disables Topology Validation.
--- name: Match anything interfaces: - any: any:any
Example 2: Match any interface on a specific neighbor
This pattern says, the node being provisioned must be connected to a neighbor with hostname pod1-spine1 but it can be connected to any peer interface.
--- name: Anything on pod1-spine1 interfaces: - any: pod1-spine1:any
Example 3: Match specific interface on a specific neighbor
This pattern says, the node being provisioned must be connected to a neighbor with hostname pod1-spine1 on Ethernet1.
--- name: Anything on pod1-spine1 interfaces: - any: pod1-spine1:Ethernet1
Example 4: Make sure I’m not connected to a node
This pattern is the same as Example #2, but we add another check to make sure the node being provisioned is not connected to any spines in pod2.
--- name: Not connected to anything in pod2 interfaces: - any: pod1-spine1:any - any: regex('pod2-spine\d+'):none - none: regex('pod2-spine\d+'):any #equivalent to line above
Example 5: Using variables in the pattern
This pattern is similar to what you’ve seen above except we use variables to make things easier.
--- name: Not connected to any spine in pod2 variables: - not_pod2: regex('pod2-spine\d+') interfaces: - any: pod1-spine1:any - any: $not_pod2:none
Pattern files are YAML-based and are the underpinnings of Topology Validation. A node will not be successfully provisioned if it cannot pass all of the interface tests contained within the pattern file. The examples above are just a small sample of the complex associations you can create. Take a look at the neighbordb section to learn more.
YAML can be a pain, and invalid YAML syntax will cause provisioning to fail. You can make sure your syntax is correct by using a tool like YAMLlint
Aside from sending the node a startup-config, I’d like to upgrade the node to a specific v(EOS) version.
These types of system changes are accomplished via the definition file. The definition is a YAML-based file with a section for each action that you want to execute.
Learn more about Actions.
# Go to your data_root - by default it's /usr/share/ztpserver admin@ztpserver:~# cd /usr/share/ztpserver # Create an images directory admin@ztpserver:~# mkdir -p files/images # SCP your SWI into the images directory, name it whatever you like admin@ztpserver:~# scp admin@otherhost:/tmp/vEOS.swi files/images/vEOS_4.14.5F.swi
Now let’s create a definition that performs the install_image action:
# Go to your data_root - by default it's /usr/share/ztpserver admin@ztpserver:~# cd /usr/share/ztpserver # Move to the specific node directory that you created earlier admin@ztpserver:~# cd nodes/<SYSTEM_ID> # Create a definition file admin@ztpserver:~# vi definition
Add the following lines to your definition, changing values where needed:
--- name: static node definition actions: - action: install_image always_execute: true attributes: url: files/images/vEOS_4.14.5F.swi version: 4.14.5F name: "Install 4.14.5F"
The definition is where we list all of the actions we want the node to execute during the provisioning process. In this case we are hosting the SWI on the ZTPServer, so we just define the url in relation to the data_root. We could change the url to point to another server altogether - the choice is yours. The benefit in hosting the file on the ZTPServer is that we perform an extra checksum step to validate the integrity of the file.
In practice, the node requests its definition during the provisioning process. It sees that it’s supposed to perform the install_image action, so it requests the install_image python script. It then performs an HTTP GET for the url. Once it has these locally, it executes the install_image script.
I want to use variables in my definition and abstract the values to a unique file. These variables will be sent down to the node during provisioning and be used while the node is executing the actions listed in the definition.
Create a file named attributes in [data_root]/nodes/<SYSTEM_ID>/.
Here’s the different type of ways to define the attributes:
Example 1: A simple key/value pair
--- ntp_server: ntp.example.com dns_server: ns1.example.com
Example 2: key/dictionary
--- system_config: ntp: ntp.example.com dns: ns1.example.com
Example 3: key/list (note the hyphens)
--- dns_servers: - ns1.example.com - ns2.example.com - ns3.example.com - ns4.example.com
Example 4: Referencing another variable
--- ntp_server: ntp.example.com other_var: $ntp_server
Borrowing from the definition recipe above, we can replace some values with variables from the attributes file:
--- name: static node definition actions: - action: install_image always_execute: true attributes: url: $swi_url version: $swi_version name: $swi_name
and the nodes/<SYSTEM_ID>/attributes
--- swi_url: files/images/vEOS_4.14.5F.swi swi_version: 4.14.5F swi_name: "Install 4.14.5F"
The attributes file is optional. The variables that are contained within it are sent to the node during provisioning. In the final example above you can see how the attributes file and definition work in concert. Note that the ZTPServer performs variable substitution when the node requests the definition via GET /nodes/<SYSTEM_ID>. By removing the static values from the definition, we can use the same definition for multiple nodes (using symlink) and just create unique attributes files in the node’s directory.
It’s important to note that these variables can exist in different places and accomplish the same task. In this recipe we created a unique attributes file, which lives in the node’s directory. You can also put these attributes directly into the definition file like the example below.
Example: At the global scope of the definition
--- name: static node definition actions: - action: install_image always_execute: true attributes: url: $swi_url version: $swi_version name: $swi_name attributes: swi_url: files/images/vEOS_4.14.5F.swi swi_version: 4.14.5F swi_name: "Install 4.14.5F"