Skip to content

dvermagithub/zsnow

Repository files navigation

zsnow

Source-available bridge that brings Zerto disaster-recovery state into ServiceNow. Business Source License 1.1 — free for non-production use; commercial license required for production. Zerto-first.

Status: v0.1 implemented end-to-end. All seven v0.1 flows shipped; 100 tests passing. Verified against ServiceNow Zurich/Yokohama PDIs and Zerto v10.8 Linux Appliances. SNOW-side install is fully automated as of 0.1.2.


What zsnow looks like in SNOW

The bridge runs in the customer's network next to the Zerto ZVM and reconciles state into ServiceNow on a polling cadence. From the operator's perspective, SNOW is the only thing they touch.

Zerto alerts land in SNOW Event Management

Every Zerto alert (RPO breach, peer-site loss, FOT-reminder overdue, license expiry...) becomes an em_alert. The bridge transitions alerts cleanly when Zerto resolves them — no duplicates, no manual ack-ack-ack.

SNOW em_alert list, filtered to source=zsnow, showing 9 Zerto alerts at various severity levels

Zerto VPGs become first-class CMDB CIs

A custom CI class u_cmdb_ci_zerto_vpg (extends cmdb_ci) is created on install. Every VPG is upserted with its full Zerto configuration, then member VMs are linked to existing cmdb_ci_vm_instance rows. The bridge does NOT create VM CIs — vCenter is the source of truth for VM identity.

Zerto VPGs CMDB list showing 16 VPGs synced from a Zerto appliance

Each VPG record exposes the full Zerto configuration plus 5 action buttons wired to bridge endpoints. Refresh from Zerto pulls fresh detail on demand; the FOT lifecycle buttons drive ServiceNow's Change Management workflow.

Zerto VPG record form showing all Zerto fields populated plus action buttons at the top

Failover Tests are SNOW-driven, end-to-end

Click "Zerto - Request Test Failover" on a VPG → bridge creates a change_request with the FOT pre-staged. Run it through your existing CM approval workflow. After approval, click "Run Test Failover" to actually fire the test in Zerto. Click "Stop FOT - Successful" or "Stop FOT - Failed" to record the result. The operator never touches the Zerto GUI.

New change_request created by Request Test Failover, in New state with workflow guidance in the banner

Every Zerto Failover Test, Failover, AND Move task gets a corresponding change_request — whether initiated from SNOW or directly in Zerto. The bridge mirrors task state in real time and closes the change when the task terminates. Type mapping in v0.1:

  • Failover Testchange_request.type = normal
  • planned Failover / Movechange_request.type = normal
  • unplanned Failover (Zerto auto-failover) → change_request.type = emergency

This is Flow 4 — the audit lane. It runs independently of Flow 5 (the SNOW-initiated FOT button); together they ensure every Zerto operation lands in Change Management regardless of where it was started.

Change Request list filtered to zsnow-task correlation, showing Zerto Failover Tests, Failovers, and Moves


What it does

zsnow is a long-running async Python process that runs inside the customer's network next to the Zerto ZVM and reconciles Zerto state into ServiceNow:

# Flow What it does
1 Alerts → em_event Poll /v1/alerts every 60s, drive SNOW Event Management state via transition-only writes
2 Bidirectional close SNOW operator closes em_alert → bridge POSTs /v1/alerts/{id}/dismiss back to Zerto
3 VPG reconcile → CMDB Cheap-tick every 5 min + deep-detail sweep every hour + on-demand pull via SNOW UI
4 Failover / Move tasks → change_request Mirror Zerto-initiated FOT/FOL/Move as auto-record change_requests, type=emergency for unplanned failovers
5 Run/Stop FOT from SNOW Full SNOW-driven Failover Test lifecycle: Request → Approve → Run → Stop Successful/Failed
6 Deterministic CMDB enrichment Walk CMDB on each alert to annotate em_event with business service / owner / tier
7 Audit log Every write captured in SNOW u_zerto_audit + local SQLite intent log

The bridge is level-triggered and stateless — restart it, lose its SQLite, redeploy elsewhere, all no-ops. SNOW is the state store. Read the architecture deep-dive for why.


About the SNOW MID Server (the one required prerequisite)

The MID Server is ServiceNow's lightweight Java agent that runs inside the customer's network. It's the standard way SNOW integrates with on-prem systems — Discovery uses it, Orchestration uses it, every ITOM integration uses it. zsnow uses it for the same reason: it makes the integration zero-pinhole at the customer's edge.

What the MID Server gives you, deployed alongside zsnow:

Without a MID Server With a MID Server
SNOW can't reach the bridge unless you punch an inbound firewall hole MID Server polls SNOW outbound only — no inbound firewall hole needed
Refresh from Zerto, Run Test Failover, Stop FOT buttons in SNOW have no path to call the bridge Those buttons work via the MID Server's ecc_queue channel
Bridge can only push data INTO SNOW; SNOW can't initiate anything against Zerto Full bidirectional control — SNOW operators drive Failover Tests, dismiss alerts, refresh CMDB on demand
Customer security team has to approve an inbound firewall change for every PDI / SaaS region No change required; uses the same outbound HTTPS path the MID Server already uses for everything else

For many enterprises this is the difference between "we can deploy zsnow" and "we can't, our security team won't approve the inbound rule." If you already have a MID Server for any other reason (Discovery, Orchestration, Cloud Provisioning) — you can reuse it; nothing zsnow-specific has to be installed on it.

Install path: zsnow does NOT ship a MID Server installer. The download URL is date-stamped (no stable "latest" link), the registration step is interactive, and most enterprises already have an in-house playbook. Follow ServiceNow's docs (search "MID Server install") or use the known-good prompt-answers in snow-app/mid-server/example-install.md.

The MID must be Up + Validated in your PDI before running the bridge install below.


Quickstart

The install is two halves: install + validate a MID Server, then run the bridge against it. Total ~30 minutes including the MID Server registration wait.

Step 1 — Create the MID Server's SNOW account

The MID Server logs into SNOW as a dedicated service account. Create it BEFORE installing the agent so the installer has somewhere to authenticate.

In SNOW navigate to sys_user.listNew and fill in:

Field Value
User ID mid.server.zsnow
First name MID
Last name Server
Identity type Machine (Zurich+; replaces the old "Web service access only" checkbox)
Internal Integration User ✅ checked (keeps this user off the licensed-seat count)
Active

Submit. Then click Set Password on the saved record. Then scroll to the Roles related list at the bottom → Edit → add the role mid_server → Save.

⚠️ Password charset matters. ServiceNow's "Generate password" button produces values full of special chars ($, !, <, &, {, }, ;) that break the MID installer's config.xml and shell expansion. Use an alphanumeric-only password — openssl rand -hex 16 produces a clean 32-character one.

Step 2 — Install the SNOW MID Server agent

zsnow doesn't ship a MID Server installer (the download URL is date-stamped and the registration step is interactive). Follow ServiceNow's docs (search "MID Server Linux install") OR the field-by-field walkthrough at snow-app/mid-server/example-install.md.

When the installer prompts, use these exact values (the rest of the install assumes them):

Prompt Answer Why
ServiceNow Instance URL https://<your-pdi>.service-now.com Your PDI
Username for mid user mid.server.zsnow Matches Step 1
Password for mid user (paste from Step 1)
MID Server Name Zerto-Prod Must match ZSNOW_MID_AGENT= in .env (Step 3)
Unique service name mid_zerto_prod Underscores only — installer rejects dashes
Long service name Zerto_Prod_MID_Server Cosmetic; shown in systemctl status long output
Non-root user <your-vm-user> (e.g. zerto) The agent runs as this user
Block all other user access? yes Locks down /opt/servicenow/mid/agent/ so only this user can read config.xml (where the password lives plaintext)

After install, validate the MID Server in SNOW:

  1. Open ecc_agent.list in SNOW
  2. Find the Zerto-Prod row
  3. Click into it → click Validate (top-right of the form)
  4. Status should flip from "Up, Validated: No" to "Up, Validated: Yes" within ~30 seconds. If it hangs, restart the agent on the VM: sudo systemctl restart mid_zerto_prod

Step 3 — Install zsnow

# 1. Get .env in place — copy template, fill in real values
cp .env.example .env
$EDITOR .env

Required .env values (the script validates these at startup and fails fast if any are missing or still placeholders):

Variable Example Notes
ZERTO_APPLIANCE_URL https://192.0.2.10 Your Zerto ZVM (v10+) over HTTPS
ZERTO_USERNAME / ZERTO_PASSWORD admin / <your-pw> Keycloak-backed ZVM admin user
VCENTER_HOST 192.0.2.20 For VPG-member VM CMDB enrichment
VCENTER_USERNAME / VCENTER_PASSWORD administrator@vsphere.local / <your-pw>
SNOW_INSTANCE_URL https://<your-pdi>.service-now.com
SNOW_USERNAME / SNOW_PASSWORD admin / <your-pw>
ZSNOW_API_TOKEN <openssl rand -base64 32> Shared secret between bridge and MID Server
ZSNOW_BRIDGE_LAN_URL http://192.0.2.50:8088 Where MID Server reaches the bridge
ZSNOW_MID_AGENT Zerto-Prod Must match the MID Server Name from Step 2
# 2. One command does the rest
./install-bridge.sh

install-bridge.sh pulls the Docker image, starts the container, runs ensure-cmdb-schema (custom tables + columns), and runs ensure-snow-config (sys_properties + Script Includes + UI Actions + form layouts). It's idempotent: re-run it to upgrade or to repair drift.

If you'd rather type out each step (or you're integrating with your own CI/CD):

docker run -d --name zsnow --restart unless-stopped \
    --env-file .env -p 8088:8088 \
    -v zsnow-data:/home/zsnow/app/var \
    ghcr.io/dvermagithub/zsnow:0.1.2

docker exec zsnow python -m zsnow ensure-cmdb-schema
docker exec zsnow python -m zsnow ensure-snow-config

curl http://localhost:8088/healthz

Full Docker options (Compose, local builds, healthcheck details): deploy/docker/README.md.

From source

git clone https://github.com/dvermagithub/zsnow.git
cd zsnow
conda create -n zsnow python=3.12 -y
conda activate zsnow
pip install -r requirements.txt
pip install -e .

cp .env.example .env
$EDITOR .env

python -m zsnow ensure-cmdb-schema
python -m zsnow ensure-snow-config
python -m zsnow run

python -m zsnow stop cleanly terminates a running bridge by PID file.


Architecture at 30,000 ft

Customer datacenter (private)                     ServiceNow SaaS
─────────────────────────────                     ───────────────
Zerto ZVM                                         <your-pdi>.service-now.com
       ▲                                                ▲
       │ HTTPS                                          │ HTTPS  (basic for v0.1, OAuth2 in v1.0)
       │                                                │
       │                                       outbound only
       └────── zsnow bridge ───── SNOW Table API ───────┘
                  │
                  ▼
            MID Server  ◄────── inbound SNOW UI Actions
                  │            (no firewall pinholes;
                  ▼             MID Server polls SNOW)
            zsnow bridge again, via ecc_queue

The MID Server path exists so SNOW UI Actions ("Refresh from Zerto", "Run Test Failover", "Stop FOT") can reach the bridge without opening an inbound firewall port on the customer's edge. The MID Server polls SNOW outbound; SNOW puts work in ecc_queue; the MID picks it up and calls the bridge over LAN. See docs/deployment.md for production deployment options.


Repo tour

Path What's there
install-bridge.sh One-command install of the bridge half: pull image, start container, run ensure-cmdb-schema and ensure-snow-config.
src/zsnow/ The Python bridge — pollers, adapters, ServiceNow client, FastAPI surface
tests/ Pytest suite — 100 tests, pure-function reconciler tests + API tests against fake sources
docs/architecture.md Why the bridge is level-triggered, stateless, content-derived correlation keys
docs/deployment.md Production deployment options (MID Server, reverse proxy, mesh VPN)
docs/zerto-api-deficiencies.md Catalog of Zerto API gaps that forced our design
docs/snow-links.md SNOW URL cheat-sheet for the records the bridge touches
snow-app/mid-server/ SNOW Script Includes, UI Actions, install cookbooks (the source of truth for what ensure-snow-config installs)
deploy/docker/ Container deployment guide
.env.example Full reference of every env var the bridge reads
CHANGELOG.md Release notes — 0.1.0 → 0.1.1 → 0.1.2; known issues per version

License

Business Source License 1.1 — non-production use is free; any production use requires a commercial license. Each release auto-converts to Apache License, Version 2.0 four years after publication (the 0.1.x line sunsets on 2030-06-08).

For a commercial production license, contact: dverma.wi@gmail.com

Contributions

External contributions are welcome but require a signed Contributor License Agreement (CLA) before they can be merged. The CLA confirms you have the right to contribute the code and that you grant the project the same rights to your contribution as the LICENSE grants to recipients. See CONTRIBUTING.md.


Contributing / questions

This is an early open-source release. Bug reports, install feedback, and PRs welcome — open an issue on GitHub. Please don't include real PDI credentials or Zerto API tokens in issue text or screenshots.

About

Source-available bridge that brings Zerto disaster-recovery state into ServiceNow. BUSL 1.1.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors