-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
24a2c01
commit 8538c8e
Showing
4 changed files
with
225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# FreeZTP Provisioning Watcher | ||
|
||
Watches specified directory for [FreeZTP][freeztp] custom merged-config files which are created after a switch is successfully provisioned. File name is parsed for hostname and host IP address to initiate a TFTP transfer of the specified IOS image. | ||
|
||
_**Use-case**_: Copy IOS image .bin file to C2960X switch post FreeZTP provisioning to avoid the auto-install function using a .tar file (lengthy process). | ||
|
||
## Considerations | ||
|
||
- Ensure that FreeZTP **imagediscoveryfile-option** is set to **disable**. | ||
|
||
```bash | ||
ztp set dhcpd INTERFACE-{dhcp_interface} imagediscoveryfile-option disable | ||
``` | ||
|
||
- Custom merged-config file syntax must begin with **{{keystore_id}}_{{ipaddr}}**; e.g. | ||
|
||
`{{keystore_id}}_{{ipaddr}}_{{idarray|join("-")}}_merged.cfg` | ||
|
||
_**Full custom log file config example...**_ | ||
|
||
```bash | ||
ztp set logging merged-config-to-custom-file '/etc/ztp/logs/merged/{{association}}/{{keystore_id}}_{{ipaddr}}_{{idarray|join("-")}}_merged.cfg' | ||
``` | ||
|
||
\*_**Suggestion**_: Disable logging merged configs to the main log file via; | ||
|
||
```bash | ||
ztp set logging merged-config-to-mainlog disable | ||
``` | ||
|
||
## Installation/Usage | ||
|
||
1. Clone repo to desired location. | ||
|
||
```bash | ||
sudo git clone {URL} /var/git/ztp-watcher | ||
``` | ||
|
||
2. Edit **ztp-watcher.service** systemd unit file with path. | ||
|
||
```bash | ||
sudo nano /var/git/ztp-watcher/ztp-watcher.service | ||
``` | ||
|
||
- _**Edit `ExecStart` and `WorkingDirectory` paths accordingly**_ | ||
|
||
```bash | ||
... | ||
ExecStart=/bin/bash -c 'cd /var/git/ztp-watcher; python3 ztp-watcher.py' | ||
WorkingDirectory=/var/git/ztp-watcher/ | ||
... | ||
``` | ||
|
||
3. Make a copy of **ztpconfig_sample.yaml** as **ztpconfig.yaml** and edit for environment. | ||
|
||
```bash | ||
sudo cp /var/git/ztp-watcher/ztpconfig_sample.yaml /var/git/ztp-watcher/ztpconfig.yaml | ||
sudo nano /var/git/ztp-watcher/ztpconfig.yaml | ||
``` | ||
|
||
- _**Edit values accordingly**_ | ||
> **watch_dir** must match path from the `ztp set logging merged-config-to-custom-file` path. | ||
|
||
```yaml | ||
logfile: '/etc/ztp/logs/ztpwatcher.log' | ||
watch_dir: '/etc/ztp/logs/merged/' | ||
tftpaddr: '172.17.251.251' | ||
imgfile: 'c2960x-universalk9-mz.152-4.E8.bin' | ||
username: 'cisco' | ||
password: 'cisco' | ||
``` | ||
|
||
5. Copy **.service** file to **/etc/systemd/system/**, then enable and start it. | ||
|
||
```bash | ||
cp /{path}/ztp-watcher.service /etc/systemd/system/ | ||
sudo systemctl enable ztp-watcher.service | ||
sudo systemctl start ztp-watcher.service | ||
``` | ||
|
||
## References | ||
|
||
- https://github.com/PackeTsar/freeztp/ | ||
- https://github.com/torfsen/python-systemd-tutorial | ||
- https://pynet.twb-tech.com/blog/nornir/intro.html | ||
- https://pynet.twb-tech.com/blog/nornir/os-upgrade-p1.html | ||
- https://www.michaelcho.me/article/using-pythons-watchdog-to-monitor-changes-to-a-directory | ||
|
||
|
||
|
||
|
||
[freeztp]: https://github.com/PackeTsar/freeztp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#!/usr/bin/python3 | ||
# Author: DS, Synergy Information Solutions, Inc. | ||
|
||
|
||
import time | ||
import os | ||
import threading | ||
import logging | ||
import yaml | ||
from watchdog.observers import Observer | ||
from watchdog.events import FileSystemEventHandler | ||
from nornir import InitNornir | ||
from nornir.plugins.tasks.networking import netmiko_send_command | ||
|
||
|
||
with open('./ztpconfig.yaml', 'r') as f: | ||
config = yaml.safe_load(f) | ||
|
||
logfile = config['logfile'] | ||
watch_dir = config['watch_dir'] | ||
tftpaddr = config['tftpaddr'] | ||
imgfile = config['imgfile'] | ||
username = config['username'] | ||
password = config['password'] | ||
|
||
ignorefiles = ['.swp', '.save'] | ||
|
||
|
||
def std_log(agg_result): | ||
for k, multi_result in agg_result.items(): | ||
for result_obj in multi_result: | ||
Logger(f'{k}\n{result_obj.result}') | ||
|
||
|
||
class Logger: | ||
|
||
def __init__(self, logdata): | ||
logging.basicConfig(format='\n%(asctime)s %(message)s', | ||
datefmt='%m/%d/%Y %I:%M:%S %p', | ||
filename=logfile, | ||
level=logging.INFO) | ||
logging.info(f'-- {logdata}') | ||
|
||
|
||
class Watcher: | ||
|
||
def __init__(self): | ||
self.observer = Observer() | ||
|
||
def run(self): | ||
|
||
event_handler = Handler() | ||
self.observer.schedule(event_handler, watch_dir, recursive=False) | ||
self.observer.start() | ||
Logger('Starting FreeZTP Provisioning Watcher.') | ||
try: | ||
while True: | ||
time.sleep(5) | ||
|
||
except KeyboardInterrupt: | ||
self.observer.stop() | ||
print('\nKeyboard interrupt.') | ||
Logger('Stopping FreeZTP Provisioning Watcher (Keyboard interrupt).') | ||
|
||
except: | ||
self.observer.stop() | ||
print('Error.') | ||
Logger('Error.\n') | ||
|
||
|
||
class Handler(FileSystemEventHandler): | ||
|
||
def os_upgrade(self, hostname, hostaddr, tftpaddr, imgfile): | ||
nr = InitNornir( | ||
inventory={ | ||
'options': { | ||
'hosts': { | ||
hostname: { | ||
'hostname': hostaddr, | ||
'username': username, | ||
'password': password, | ||
'platform': 'ios' | ||
} | ||
} | ||
} | ||
} | ||
) | ||
result = nr.run( | ||
task=netmiko_send_command, | ||
command_string=f'copy tftp://{tftpaddr}/{imgfile} flash:', | ||
delay_factor=6, | ||
) | ||
std_log(result) | ||
|
||
def on_created(self, event): | ||
|
||
if event.is_directory: | ||
return None | ||
|
||
else: | ||
newfile = event.src_path.rpartition('/')[2] | ||
if not any(str in newfile for str in ignorefiles): | ||
Logger(f'File created: {newfile}') | ||
hostname = newfile.split('_')[0] | ||
hostaddr = newfile.split('_')[1] | ||
Logger(f'Transferring file to {hostname} (IP: {hostaddr}).') | ||
x = threading.Thread(target=self.os_upgrade, args=( | ||
hostname, hostaddr, tftpaddr, imgfile)) | ||
x.start() | ||
|
||
|
||
if __name__ == '__main__': | ||
w = Watcher() | ||
w.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[Unit] | ||
Description=FreeZTP Provisioning Watcher | ||
After=network.target | ||
|
||
[Service] | ||
ExecStart=/bin/bash -c 'cd /var/git/ztp-watcher; python3 ztp-watcher.py' | ||
WorkingDirectory=/var/git/ztp-watcher/ | ||
Environment=PYTHONUNBUFFERED=1 | ||
Type=simple | ||
Restart=on-abort | ||
|
||
[Install] | ||
WantedBy=multi-user.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
logfile: /etc/ztp/logs/ztpwatcher.log | ||
watch_dir: /etc/ztp/logs/merged/ | ||
tftpaddr: 172.17.251.251 | ||
imgfile: c2960x-universalk9-mz.152-4.E8.bin | ||
username: cisco | ||
password: cisco |