The Wayback Machine - https://web.archive.org/web/20201203102433/https://github.com/nchekwa/python-ztp
Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Zero touch provisioning (ZTP) (include DHCP/TFTP/HTTP)

Python3 script which using YAML file as host config to offer DHCP/IP for device.
System sniff DHCP packets and answer using own build DHCP responses (based on scapy library)
Built-in TFTP/HTTP servers allow host download config/firmware which was pointed in dhcp option.

No more installing Isc/Kea DHCP and configuring DHCP scopes/TFTP/HTTP...

Syntax:

[root@server ztp]# ./ztp.py -h
usage: ztp.py [-h] [-i INTERFACE] [-l LIMIT] [-p PATH] [--port_tftp PORT_TFTP]
              [--port_http PORT_HTTP] [-d PCAP]

optional arguments:
  -h, --help            show this help message and exit
  -i INTERFACE, --interface INTERFACE
                        Interface to service requests
  -p PATH, --path PATH  TFTP folder path. Set `None` to disable TFTP
  --port_tftp PORT_TFTP
                        TFTP port
  --port_http PORT_HTTP
                        HTTP port
  -d PCAP, --pcap PCAP  collect PCAP file name for debug

Memory consumption:

[root@server ztp]# pmap 3068 | grep total
 total           410900K

Example:

Screenshot

Config Yaml file example:

VM0021210000:
  mac: 50:00:21:21:00:00
  hostname: Spine-21
  domain: juniper.lab
  ip: 10.240.40.21
  subnet_mask: 255.255.252.0
  router: 10.240.40.1
  name_server: 8.8.8.8       // (only SINGLE server supported)
  lease_time: 60
  tftp_server_name: 10.240.40.254
  boot-file-name: juniper.config
  tftp_server_address: 10.240.40.254
  vendor_specific: 0:junos.tgz,1:VM0021210000/juniper.sh,3:http

Config needs to be store in tftp folder in single / or multiple yaml files.
Configs are checked/read everytime when new dhcp Discovery pacet would be capture.
This consuming some resorces (utlize disk) but allow you 'in fly' modyfie config without restarting ztp instance.
YAML Config format:
<hostname/name>:
  mac: <mac - allow format with seperators like .:->
  <parameter>: <value>

In some cases new unboxed device report as hostname their serial number so in this situation, if script discovery that hostname inside Discovery message match this <hostname/name> - it will use this config section to send Offer.

DHCP_Discovery

If hostname/name will not be found in config files - it will check in 2nd step - mac match - for DHCP request in all configs.

parameters are related to scapy dhcp layer lib which can be found under this Link -> DHCPOptions
Please note that parameters tftp_server_name (option 66) and tftp_server_address (option 150) are missing in scapy layer lib so it needs to be added manually:

sed -i 's/    67: StrField/    66: "tftp_server_name",\n    67: StrField/g' /usr/local/lib/python3.6/site-packages/scapy/layers/dhcp.py
sed -i 's/    255: "end"/    150: IPField("tftp_server_address", "0.0.0.0"),\n    255: "end"/g' /usr/local/lib/python3.6/site-packages/scapy/layers/dhcp.py

Scapy issie 2747

Docker

Please remember that this docker need to be exposed to net=host as we need to get access to broadcast packages (scapy promisc mode). Use IFACE env on which interface docker should be listening.

Download docker image:

Download from docker.hub

[user@localhost ~]# docker pull nchekwa/python_ztp

Run in interactive mode:

[user@localhost ~]# docker run --name ztp --net=host -e IFACE="eth1" -v /tftp:/opt/ztp/tftp --rm -ti nchekwa/python_ztp

Docker registration:

[user@localhost ~]# docker create \
  --name=ztp \
  --net=host \
  -e IFACE="eth1" \
  -v /tftp:/opt/ztp/tftp \
  --restart unless-stopped \
  nchekwa/python_ztp:latest

[user@localhost ~]# docker ps -a
CONTAINER ID        IMAGE                       COMMAND                   CREATED              STATUS              PORTS               NAMES
22cf97689207        nchekwa/python_ztp:latest   "/bin/sh -c '\"python…"   About a minute ago   Created                                 ztp

[user@localhost ~]# docker start ztp
ztp
[user@localhost ~]#  docker logs -f --tail 10 ztp
Running. Press CTRL-C to exit.
[facts]    DHCP/TFTP interface: eth1
[facts]    eth1 IP: 10.240.40.254
[facts]    eth1 MAC: 50:00:00:33:00:01
[facts]    File folder status: exist
[facts]    File Path: /opt/ztp/tftp
[OK]       TFTP starting on 10.240.40.254 - port 69
[Warning]  HTTP 10.240.40.254:80 port in use
[OK]       DHCP starting sniffer eth1 - udp and (port 67 or 68)
eth1|<- [Rcv][Discover][0x61ec0f9e] Host VM0033330000 (50:00:33:33:00:00) asked for an IP
eth1|<- [Rcv][Discover][0x7c78d728] Host VM0033330000 (50:00:33:33:00:00) asked for an IP

Docker Composer:

Install:

curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

docker-compose --version
docker-compose version 1.26.2, build eefe0d31

Run:

docker-compose.yaml

---
version: "3"
services:
  python_ztp:
    image: nchekwa/python_ztp
    container_name: ztp
    network_mode: host
    environment:
      - IFACE=eth1
    volumes:
      - /tftp:/opt/ztp/tftp
    restart: unless-stopped
[user@localhost ~]# docker-compose -f docker-compose.yaml up -d
[user@localhost ~]# docker-compose -f docker-compose.yaml down
You can’t perform that action at this time.