Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Juniper Multi-Platform Upgrade Utility

Automated staged software upgrade tool for Juniper EX, MX, and SRX platforms.
Installs software to the pending partition (no reboot) — reboot is performed separately during a maintenance window.


⚠ DISCLAIMER — READ BEFORE USE

Use this tool at your own risk.

This utility automates software upgrades on live network infrastructure. Improper use, untested configurations, or unexpected device behavior can result in network outages, devices failing to boot, or loss of connectivity to managed equipment.

Before using this tool on any production network:

  1. Run dry-runs first — always validate with --dry-run before touching any device. Confirm the upgrade paths, package files, and device connectivity all look correct.

  2. Test on a single non-critical device — run a real upgrade on one lab or low-impact device first. Observe the full output, verify the device reboots cleanly on the new version, and confirm network behavior is as expected before upgrading anything else.

  3. Understand your environment — behavior may vary across software versions, hardware revisions, configurations, and network topologies. What works in a lab may behave differently in production. Know your devices before you run this at scale.

  4. Have a rollback plan — the script issues request system software rollback automatically on failure, but this does not reboot the device. Ensure you have out-of-band console access or a maintenance window where a manual reboot is acceptable.

  5. Parallel mode carries higher risk — upgrading many devices simultaneously means a misconfiguration or unexpected error affects multiple devices at once. Use sequential mode until you are confident the tool behaves correctly in your environment.

No warranty is provided. This tool is offered as-is. The authors accept no responsibility for outages, data loss, or any other damage resulting from its use.


Supported Platforms

Platform Junos Model String (examples) Package Filename
EX2300-C ex2300-c-12p, ex2300-c-12t junos-arm-32-{version}.tgz
EX2300 ex2300-24p, ex2300-48p junos-arm-32-{version}.tgz
EX3400 ex3400-24p, ex3400-48p junos-arm-32-{version}.tgz
EX4100 ex4100-24p, ex4100-48p, ex4100-h-* junos-install-ex-arm-64-{version}.tgz
EX4600 ex4600-40f jinstall-host-ex-4600-{version}-signed.tgz
EX4650 ex4650-48y jinstall-host-ex-4e-x86-64-{version}-secure-signed.tgz
MX204 mx204 junos-vmhost-install-mx-x86-64-{version}.tgz
SRX300 srx300, srx320, srx340, srx345 junos-srxsme-{version}.tgz

EX4100-H note: The EX4100-H variant is currently mapped to the same EX4100 package.
Verify compatibility before upgrading — update PLATFORM_MAP in the script to split if needed.

Model detection is automatic — the script connects to each device and reads the model
string directly from Junos. You never need to specify the model in the targets file.

Unsupported platforms — if the script encounters a model it does not recognize, the
dry-run report provides exact step-by-step instructions to add support: FTP directory to
create, package filename format, and the two code changes needed in the script.


FTP Server Layout

The script uses anonymous FTP (no credentials) to scan available versions.
Default server: 192.168.10.1 (override with --ftp-server).

vsftpd is configured with anon_root=/srv/ftp, so the client-visible path starts at /code/:

/srv/ftp/                        <- vsftpd anon_root (chmod 755, not writable — required by vsftpd)
└── code/
    ├── EX2300/                  <- EX2300 and EX2300-C both use this directory
    ├── EX3400/
    ├── EX4100/
    ├── EX4600/
    ├── EX4650/
    ├── MX204/
    └── SRX300/

Each directory may contain multiple version files. Drop new .tgz files in and the
script detects them automatically on the next run — no configuration changes needed.

Uploading code files: Use your local Linux user account (must be in the ftp group).
The script connects as anonymous and is read-only — it never writes to the FTP server.


NETCONF Requirement

The script connects to devices via NETCONF on port 830, not regular SSH on port 22.
Both services must be enabled on every target device:

configure
set system services ssh
set system services netconf ssh
set system services netconf rfc-compliant
commit

If NETCONF is not enabled the script detects it automatically and prints the exact
commands needed to fix it. Add these to your ZTP staging config (access.conf) so
all future devices get NETCONF enabled automatically during ZTP provisioning.


Rule of 3 — Upgrade Path Planning

Juniper recommends not skipping more than 3 available releases in a single upgrade.

The script scans the FTP directory, sorts all available versions, and plans the path:

  • <=3 available releases behind latest -> direct upgrade in one run.
  • More than 3 behind -> stages the 3rd-closest available release, stops, and tells
    you to reboot and re-run. The next run automatically plans the next hop.

One hop per run — always. The script never chains hops automatically. You reboot,
verify the device came up healthy on the new version, then re-run for the next hop.
This ensures each upgrade is verified before committing to the next one.

Example — FTP has: 21.4, 23.4 — Device running 18.1:

Run 1:  18.1 -> 21.4   staged, reboot, verify
Run 2:  21.4 -> 23.4   staged, reboot, done

The dry-run validation report shows the full multi-hop plan and verifies every
package file in the chain with a checkmark before you touch any device.


Requirements

Python packages

pip install junos-eznc lxml paramiko --break-system-packages

Or use the setup script (recommended — does everything for you):

chmod +x setup.sh
sudo ./setup.sh
  • Python 3.8+
  • junos-eznc — Juniper PyEZ (pulls in ncclient automatically)
  • lxml — XML parsing for NETCONF responses
  • paramiko — SSH for OAM mount (EX2300/EX3400) and MX204 CLI install
  • ftplib, concurrent.futures — Python standard library, no install needed

Device requirements

  • NETCONF enabled (port 830)
  • SSH enabled (port 22)
  • Management IP reachable from the Pi on both ports
  • Root password required only for EX2300 / EX3400 (OAM filesystem mount)

Setup

Run once on your Raspberry Pi or jump host:

chmod +x setup.sh
sudo ./setup.sh

The setup script handles everything:

  1. Installs Python dependencies
  2. Creates all FTP directories under /srv/ftp/code/
  3. Sets correct permissions — anonymous read, local user write
  4. Adds your user to the ftp group so you can upload code files
  5. Validates vsftpd configuration and flags any issues
  6. Creates the local logs/ directory

Targets File

One IP address or hostname per line. Comments (#) are supported inline or at line start.
No model numbers needed — the script auto-detects everything from the device.

192.168.1.10    # access-sw-01
192.168.1.20    # core-sw-01 (VC pair)
192.168.1.21    # core-sw-02
192.168.1.30
192.168.1.40

Usage

First time setup

sudo ./setup.sh

Dry run — validate everything, no changes made to any device

python3 juniper_upgrade.py --targets targets.txt --dry-run

Always run dry-run first. It validates:

  • NETCONF connectivity to every device (with exact fix commands if it fails)
  • Model recognition and platform mapping
  • FTP directory accessibility and version detection
  • Every package file in the full upgrade hop chain (checkmark = present, X = missing)
  • Whether intermediate hop files exist for multi-hop upgrades
  • Devices already at latest, newer than repo, or unreachable

Real run — sequential (default, safest)

python3 juniper_upgrade.py --targets targets.txt

Upgrades one device at a time. If something goes wrong it only affects one device.

Real run — parallel (faster, all devices simultaneously)

python3 juniper_upgrade.py --targets targets.txt --parallel-upgrades

All selected devices upgrade at the same time using parallel threads. Output
may interleave during the run but the final report is always clean and ordered.
A warning is shown before the confirm prompt so you know what you are committing to.
Good for large access layer batches where all devices are the same model and you
are confident in the code. Still one hop per device per run.

All options

--targets targets.txt        Targets file (default: targets.txt)
--ftp-server 192.168.10.1    FTP server IP (default: 192.168.10.1)
--user netadmin              SSH username (prompted if omitted)
--connect-timeout 30         Seconds before giving up on a device (default: 30)
--workers 10                 Max parallel threads for discovery and parallel upgrades (default: 10)
--parallel-upgrades          Upgrade all selected devices simultaneously
--dry-run                    Validate only, no installs

Workflow

1.  Read targets.txt (IPs/hostnames, one per line, # comments ignored)
2.  Prompt for SSH username, password, root password (root for EX2300/3400 only)
3.  Create timestamped run directory: logs/YYYY-MM-DD_HH-MM-SS_{dry-run|upgrade}/
4.  Discovery pass — parallel threads, one per device:
    ├── Connect via PyEZ NETCONF (port 830)
    ├── Read model string and running version from device
    ├── Check virtual chassis membership (show virtual-chassis)
    ├── Scan FTP directory for available versions
    └── Plan upgrade path using rule of 3 (one hop per run)
5.  [--dry-run] Print full validation report with per-hop file checks, save to logs/, exit
6.  [real run] Print discovery summary table
7.  Interactive device selection — A (all), N (none), or comma-separated numbers e.g. 1,3
8.  Confirm prompt (parallel mode shows extra warning)
9.  Upgrade phase — sequential or parallel depending on flag:
    ├── OAM filesystem mount — EX2300/EX3400 only (requires root password)
    ├── VC health check — all members must be Present before proceeding
    ├── PRE-UPGRADE SAFETY (runs before every install):
    │   ├── request system configuration rescue save
    │   ├── request system snapshot all-members
    │   └── request system snapshot slice alternate
    ├── Roll back any existing pending install
    ├── Install NEXT HOP ONLY via FTP (no-validate):
    │   ├── EX2300/3400/4100/4600/4650/SRX300: PyEZ SW.pkgadd()
    │   └── MX204: paramiko CLI -> request vmhost software add <url> no-validate
    ├── Confirm Pending: version directly from show version on device
    └── On failure: request system software rollback (automatic, result shown in report)
10. Final summary report printed to screen and saved to logs/run-dir/summary.txt

Output States

State Meaning
STAGED Software staged to pending partition. Pending version confirmed from device.
FAILED Install failed. request system software rollback issued automatically.
MANUAL_REQUIRED VC member missing, OAM issue, or other condition requiring human review.
ALREADY_CURRENT Device is already running the latest version in the FTP directory.
NEWER_THAN_REPO Device is running a version newer than anything in the FTP directory.
NO_MATCH Model not recognized. Dry-run shows exact steps to add support.
FTP_ERROR FTP directory unreachable or no matching package files found.
CONNECT_ERROR NETCONF connection failed. Fix commands printed if NETCONF is not enabled.

Pre-Upgrade Safety Steps

Before every install the script automatically runs:

request system configuration rescue save
request system snapshot all-members
request system snapshot slice alternate

These are non-fatal — if a snapshot fails it is logged as a warning and the upgrade
continues. The final report shows whether they completed successfully.

Snapshot output from the device is captured and displayed, e.g.:

NOTICE: Snapshot snap.20260614.095902 created successfully

Virtual Chassis Notes

  • VC detection is automatic via show virtual-chassis RPC.
  • All VC members must be in prsnt (Present) state — upgrade aborts with
    MANUAL_REQUIRED if any member is absent.
  • Install command uses all-members flag for EX3400, EX4100, EX4600, EX4650 in VC
    so all members are staged in one operation.
  • Snapshots use all-members flag on VC devices.

EX2300 / EX3400 Special Handling

These platforms have limited flash space and require extra steps:

  1. OAM filesystem mount — the script mounts /dev/gpt/oam/ via root SSH before
    installing. Root password is required and prompted at startup. If no root password
    is provided and an EX2300/3400 is selected for upgrade, it reports MANUAL_REQUIRED.
  2. Helper packagesos-package and package-hooks may be needed on some versions.
    These are not handled by this script (see Juniper KB31198 and KB31201).

MX204 Special Handling

The MX204 uses a vmhost architecture. The install command is a CLI op command:

request vmhost software add ftp://192.168.10.1/code/MX204/<package>.tgz no-validate

This is not a standard NETCONF RPC so the script drops out of PyEZ and drives the
command via a paramiko interactive shell, streaming output until Junos confirms
completion. Install typically takes 10-20 minutes.

Rollback on failure uses request vmhost software rollback.


Run Output

Every run creates a timestamped directory:

logs/
├── 2026-06-14_15-42-11_dry-run/
│   ├── run.log        <- full terminal output, plain text (no ANSI colour codes)
│   └── summary.txt    <- clean summary of all device outcomes
└── 2026-06-14_16-05-33_upgrade/
    ├── run.log
    └── summary.txt

Failed Upgrade — Automatic Rollback

If an install returns any failure (fail, failed, exception, or ok=True with fail text
in the output), rollback is issued automatically and the result shown in the report:

✘ FAILED
  Reason         : Install of 23.4R2-S8.7 failed: ...
  ↩ ROLLED BACK  : request system software rollback — SUCCESS

  -- or --

  ↩ ROLLBACK FAILED: request system software rollback — MANUAL ACTION REQUIRED
    Run manually : request system software rollback

Unsupported Platform Handling

If the script connects to a device with an unrecognized model (e.g. ACX7100, QFX5100),
the dry-run report prints exact instructions to add support:

Status           NO MATCH — platform not supported yet

To add support for acx7100-48l:

Step 1           Create FTP directory on the Pi:
                   sudo mkdir -p /srv/ftp/code/ACX7100
                   sudo chmod 775 /srv/ftp/code/ACX7100
                   sudo chown root:ftp /srv/ftp/code/ACX7100

Step 2           Upload the Junos package to:
                   /srv/ftp/code/ACX7100/junos-{version}.tgz
                   Note: verify the correct package filename for ACX7100 on juniper.net

Step 3           Add to PLATFORM_MAP in juniper_upgrade.py:
                   "ACX7100": {
                       "ftp_dir":      "ACX7100",
                       "pkg_template": "junos-{version}.tgz",
                       "vc_flag":      False,
                       "oam_mount":    False,
                       "vmhost":       False,
                   },

Step 4           Add to detect_platform() in juniper_upgrade.py:
                   if re.match(r"acx7100", m):
                       return "ACX7100"

Adding New Code to FTP

Drop new .tgz files into the appropriate /srv/ftp/code/<PLATFORM>/ directory.
The script auto-detects all available versions on each run — no changes needed.

# Example: adding a new EX2300 release
cp junos-arm-32-24.2R1.17.tgz /srv/ftp/code/EX2300/

The next dry-run will automatically include the new version in the upgrade path planning.


Rollback Command Reference

Platform Rollback Command
EX2300, EX2300-C, EX3400, EX4100 request system software rollback
EX4600, EX4650, SRX300 request system software rollback
MX204 request vmhost software rollback

Disclaimer

This tool is provided as-is with no warranty. Always dry-run first, test on a single non-critical device, and verify the result before upgrading production infrastructure at scale. Use at your own risk. See the full disclaimer at the top of this document.# junos-upgrader

About

Version Check and Upgrader for Juniper useful with CVE-SCANNER

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages