|
This project automates the factory reset process of Cisco IR809 routers using Python over a serial connection. Instead of manually resetting each router through terminal programs like picocom or screen, the scripts handle the entire process automatically, from login and configuration cleanup to reboot and ROMMON-2 handling.
Author: Ayan Ahmed |
- Introduction
- Goals and Objectives
- Method
- Target Audience
- Document Status
- Disclaimer
- Scope and Limitations
- Environment
- Acknowledgments
- Implementaion
- Conclusion
- References
Welcome! I'm Ayan Ahmed, an IT intern at The Swedish Meteorological and Hydrological Institute (SMHI) during spring 2026. I'm studying Networking, Infrastructure and Cybersecurity at Jönköping University a program that provides both theoretical and practical knowledge in network design, IT infrastructure and cybersecurity. This is my 9-week internship project, carried out within the IT-infrastructure department. The project focuses on automating the decommissioning process of older routers stored at SMHI's facilities. Previously, resetting each router required manual work for every single device. The goal was to build a scalable and reusable solution that can be applied to future decommissioning of other router models as well.
The goal of this project is to develop an automated solution for factory resetting Cisco IR809 routers.
The solution should:
- Handle the full reset process without manual intervention
- Automatically identify the correct USB port and log in to the router
- Clean all configuration files from flash and nvram
- Handle the ROMMON-2 boot issue that arises after wiping the router
- Support parallel handling of multiple routers simultaneously
The project is divided into four sub-projects that together build a fully automated factory reset pipeline. Python was used together with the pyserial library for serial communication. Login credentials are stored securely in a .env file.Threading was used to enable parallel resets across multiple routers connected via a USB hub.
This repository is for anyone who needs to automate the factory reset of Cisco IR809 routers. It is also useful for anyone interested in Python-based serial communication and router automation in general.
This repository is considered complete and officially published. Future improvements, refinements, or corrections may be introduced through controlled updates. Any changes will be versioned and documented in the commit history.
Caution
The Scripts for this project are developed and tested against Cisco IR809 routers at SMHI facilities. It is not intended for production use without proper review and adaptation. Use at your own risk.
The solution is developed and tested specifically for the Cisco IR809 router model.
- The scripts communicate via a physical console cable over a serial connection.
- The reset process requires the router to be powered on and physically connected via USB.
- The solution does not support remote or network-based resets.
- Instructions may become outdated as software updates; always verify with official documentation.
Asus PN64 ax210NGW:
- Intel® Core™ i7-12700H
- 1TB disk
- 64 GB memory
- Operating System: Linux (Ubuntu)
Router:
- Cisco IR809G-LTE-GA-K9
- IOS Version:15.6(3)M / 15.6(3)M1b
Python:
- 3.x
Libraries:
External libraries:
- pyserial
- python-dotenv
Built-in Python modules:
- time
- os
- glob
- subprocess
- re
- threading
- sys
Connection:
- USB console cable, USB hub (for parallel handling)
I would like to thank my supervisors Robert Brokull and Rafael Urrutia Silva for their continuous support, guidance, and for providing access to the hardware needed for testing throughout this project. I would also like to thank Jonatan Högild for his support with brainstorming ideas and problem-solving during the whole process.
To avoid storing sensitive information directly in the code, login credentials are saved in a .env file locally on the machine. The Python script reads them using environment variables via os.getenv(). The file is protected by .gitignore to ensure it is never uploaded to GitHub.
bash
touch .env
nano .env
.env file structure:
ROUTER_USERNAME=username
ROUTER_PASSWORD=password
ROUTER_ENABLE_PASSWORD=enable_password
⚠️ TheROUTER_prefix is used to avoid conflicts with Linux system variables, such asUSERNAMEwhich is already reserved by the system.
Press CTRL + X, then Y, then ENTER to save and exit Nano.
bash
touch .gitignore
nano .gitignore
.env
Press CTRL + X, then Y, then ENTER to save and exit Nano.
As an alternative, Ansible Vault was also explored, which encrypts credentials unlike the .env file which stores them in plaintext. However, the .env method was chosen for its simplicity and ability to fully automate the process without manual input. The key difference is that Ansible Vault is more secure since credentials are encrypted, but requires either manual input of the vault password each time, or storing it in a separate file, which in practice gives the same security level as the .env approach.
The pyserial library was used to communicate with the router over a serial connection, replacing the manual work done in terminal programs like picocom and screen. The script automatically identifies the active USB port and logs in to the router, entering enable mode.
Installation:
bash
pip install pyserial python-dotenv
or
bash
pip3 install pyserial python-dotenv
- Create the connection script:
bash
touch connection.py
nano connection.py
- Add the following script to the file:
- Save the file:
Press CTRL + X, then Y, then ENTER.
- Test the script:
bash
python3 connection.py
Prerequisites:
- Physical console cable connected between the computer and the router
- Router powered on
- Console port identified via
ls /dev/ttyUSB*
Issues encountered:
| Issue | Solution |
|---|---|
pyserial didn't work properly |
Switched from pexpect, screen, and picocom to only using pyserial |
| Wrong USB port | Checked the correct port with ls /dev/ttyUSB* |
Device or resource busy error |
Closed active screen sessions with screen -X quit |
Wrong variable names in .env |
Made sure the variable names matched in both files |
| Timeout issues | Increased time.sleep() from 2 to 6 seconds |
Result:
1: '\r\nrt-router-hostname>\r\nrt-router-hostname>'
Logged in!
4: 'enable\r\nPassword: '
5: '\r\nrt-router-hostname#\r\nrt-router-hostname#'
Enable mode activated!
This is the core of the project. The work builds on the connection code from the previous sub-project and extends it with full configuration cleanup and reboot handling. The result is two scripts.
reset.py
Cleans the router configuration by:
- Automatically identifying the USB port using a loop
- Logging in with up to 10 attempts
- Running
write eraseto clear startup-config - Automatically identifying and removing configuration files from flash and nvram via
clean_flash()andclean_nvram() - Rebooting the router
fix_rommon.py
Handles the ROMMON-2 issue that arises after the reset by:
- Automatically detecting ROMMON-2
- Looping until the IOS file is found in flash memory
- Setting the boot variables
BOOTandBOOT_IOS_SEQUENCE - Booting the router
- Saving the boot variable permanently via
write memory
- Create the reset script:
touch reset.py
nano reset.pyThe following script was added to the file:
See Code/reset.py
- Save the file:
Press CTRL + X, then Y, then ENTER.
- Create the ROMMON fix script:
touch fix_rommon.py
nano fix_rommon.pyThe follwing script was added to the file:
4.Save the file:
Press CTRL + X, then Y, then ENTER.
- Run the reset script:
python3 reset.py- Wait a few seconds, then run the ROMMON fix script:
python3 fix_rommon.pyOr run both scripts together:
python3 reset.py && sleep 5 && python3 fix_rommon.py| Issue | Solution |
|---|---|
Router entered ROMMON-2 after write erase |
Created fix_rommon.py |
| Boot variable was not saved after reboot | Used no boot system, boot system flash:<file>, and write memory |
BOOT_IOS_SEQUENCE=0 prevented automatic boot |
Set the value to 20 |
| USB port was not found | Added a loop that retries until the port is found |
| Enable mode was not reached | Added a login loop with up to 10 attempts |
| Configuration dialog appeared after reboot | Automatically answered no |
| Not all files were deleted | Implemented clean_flash() and clean_nvram() |
| IOS file was not found on the first try | Added a loop that retries until the file is found |
To speed up the reset process when working with multiple routers, parallel_reset.py was developed. The script uses Python threading to reset several routers at the same time through a USB hub. This reduces the total time compared to resetting one router at a time.
- The number of routers is given as an argument when running the script
- The script waits until the correct number of USB ports are found
- One thread is started for each router
- Each router is reset independently
- The script prints how many routers have been completed
- Create the parallel reset script:
touch parallel_reset.py
nano parallel_reset.py- Add the following script to the file:
- Save the file:
Press CTRL + X, then Y, then ENTER.
- Run the script:
python3 parallel_reset.py- Run the script with a specific number of routers:
python3 parallel_reset.py 3| Issue | Solution |
|---|---|
| Threads interfered with each other | Each thread got its own variables and serial port |
| Completed router count was incorrect | Used threading.Lock() for safe counting |
| USB ports were not found immediately | Added a loop that waits until the correct number of ports are found |
The aim of this project was to automate the factory reset process of Cisco IR809 routers and build a reusable solution for future router decommissioning. I believe that this goal was successfully achieved.
This project was both interesting and challenging in several ways, especially when working with ROMMON-2 issues, boot variables, serial communication, and running resets in parallel. Many problems appeared during development, but solving them helped create a better understanding of how Cisco routers work internally.
The final solution supports fully automated factory resets of multiple routers at the same time, which is a clear improvement compared to the previous manual process. The result is a practical and flexible tool that can also be adapted for similar router models in future projects.
