Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Netgear Switch Manager

A web-based management tool for Netgear M4350 series managed switches. It provides a visual dashboard for monitoring and configuring multiple switches through the Netgear REST API, with a Rust CLI backend that handles all direct switch communication.

Architecture

┌──────────────────────────────────────────────────────┐
│  Browser (JavaScript SPA)                            │
│  - Port visualization with VLAN colors               │
│  - Live PoE status, neighbor tables, LAG, QoS        │
│  - Inline editing with push confirmation modal       │
│  - Auto-refresh (5s timer)                           │
└──────────────┬───────────────────────────────────────┘
               │ HTTP (Flask REST API)
┌──────────────▼───────────────────────────────────────┐
│  Python / Flask  (web/app.py)                        │
│  - Serves the SPA and static assets                  │
│  - Proxies requests to the Rust CLI via subprocess   │
│  - Manages switch inventory (config/switches.json)   │
│  - Logs all writes to config/pushlog.json            │
└──────────────┬───────────────────────────────────────┘
               │ subprocess (stdin/stdout JSON)
┌──────────────▼───────────────────────────────────────┐
│  Rust CLI  (netgear-cli)                             │
│  - Authenticates with the switch REST API            │
│  - Performs GET and POST requests                    │
│  - Outputs structured JSON to stdout                 │
└──────────────┬───────────────────────────────────────┘
               │ HTTP (Netgear REST API)
┌──────────────▼───────────────────────────────────────┐
│  Netgear M4350 Switch(es)                            │
│  - REST API on port 80                               │
│  - Session-based authentication                      │
└──────────────────────────────────────────────────────┘

Project Structure

├── config/
│   ├── switches.json        # Switch inventory (hosts, names, models)
│   ├── pushlog.json         # Audit log of all configuration changes
│   └── <SwitchName>.json    # Cached query results per switch
├── netgear-cli/
│   └── src/
│       ├── main.rs          # CLI commands (device-info, ports, poe, etc.)
│       └── api.rs           # HTTP client (login, get, post)
├── web/
│   ├── app.py               # Flask backend
│   ├── templates/
│   │   └── index.html       # SPA shell
│   └── static/
│       ├── css/styles.css
│       └── js/
│           ├── state.js     # Global state, auto-refresh
│           ├── api.js       # API call wrapper with caching
│           ├── nav.js       # Tab navigation, switch selection
│           ├── push.js      # Push confirmation modal
│           ├── tables.js    # Table toolbar (search, export)
│           ├── search.js    # Global search across cached data
│           ├── switches.js  # Add/remove switch UI
│           ├── utils.js     # Formatting helpers
│           ├── tabs/        # One file per tab (visualize, ports, poe, etc.)
│           └── editors/     # Inline edit dialogs (device name, port desc, poe toggle)
└── .env                     # SWITCH_USERNAME and SWITCH_PASSWORD

Setup

Prerequisites

  • Python 3.8+
  • Rust toolchain (for building the CLI)

Install & Run

# 1. Build the Rust CLI
cd netgear-cli
cargo build --release
cd ..

# 2. Create .env with switch credentials
echo "SWITCH_USERNAME=admin" > .env
echo "SWITCH_PASSWORD=yourpassword" >> .env

# 3. Install Python dependencies
pip install -r web/requirements.txt

# 4. Run the web server
python web/app.py
# Opens http://127.0.0.1:5000 in your browser

Adding Switches

Click + Add in the top bar, enter the switch IP address and a friendly name. The manager will discover the model, serial number, and firmware version on first query.

Web Interface Tabs

Tab Description
Visualize Color-coded port map showing VLAN assignments, link state, PoE status, and connected devices. Click a port to copy its details.
Ports Full port configuration table with descriptions, VLAN profiles, link speed, admin state. Inline editing for descriptions and profile assignments.
MAC / Neighbors Combined LLDP and ARP neighbor table. Shows connected device hostnames, MACs, IPs. Flush ARP button to clear stale entries.
PoE Power over Ethernet status per port. Toggle PoE on/off. Shows power draw, class, priority, mode.
LAG Link Aggregation Group configuration. Shows member ports, LACP mode, balance algorithm.
VLANs VLAN/profile definitions and per-port assignments. Summary view of how many ports per VLAN.
Trunks Trunk port analysis — identifies uplinks carrying multiple VLANs.
QoS Quality of Service profiles and DSCP mappings.
Push Log Audit trail of every configuration change pushed to switches.
Manage Switches Add or remove switches from the inventory.
Switch Status Device overview: model, firmware, serial, uptime, CPU, memory, fan RPM, sensor temperatures.

Top-Level Controls

  • Switch toggles — Click a switch name to select it; click All Switches to view all switches side-by-side.
  • Refresh — Clears the cache and reloads the current tab from the switch.
  • Auto-refresh (5s) — Checkbox that polls the current tab every 5 seconds.
  • Search — Searches across all cached data for IPs, MACs, VLANs, or port numbers.

Netgear M4350 REST API Reference

All endpoints below are served by the switch itself on HTTP port 80. The base URL is http://<switch-ip>.

Authentication

Every API session begins with a login. All subsequent requests must include the session token in the Session HTTP header.

POST /api/v1/login

Authenticates and returns a session token.

Request:

{
  "user": {
    "name": "admin",
    "password": "yourpassword"
  }
}

Response (success):

{
  "resp": {
    "respCode": 0,
    "respMsg": "Login successful"
  },
  "user": {
    "session": "a1b2c3d4e5f6..."
  }
}

Response (failure):

{
  "resp": {
    "respCode": -1,
    "respMsg": "Invalid credentials"
  }
}

Headers for all subsequent requests:

Session: <token from login>
Accept: application/json
Content-Type: application/json    (for POST requests)

Standard Response Envelope

Every Netgear API response wraps the result in a resp object:

{
  "resp": {
    "respCode": 0,
    "respMsg": "OK"
  },
  ...endpoint-specific data...
}
  • respCode: 0 = success
  • respCode: non-zero = error (check respMsg for details)

Some POST endpoints (such as ARP flush) return an empty HTTP body on success instead of JSON.


GET Endpoints

All GET endpoints require the Session header.

GET /api/v1/device_info

Returns switch identity, hardware details, health metrics.

CLI command: netgear-cli device-info

Response fields:

deviceInfo
├── name                    # Switch hostname
├── mac                     # Management MAC address
├── poe                     # PoE capability (boolean)
├── cpu[]
│   └── usage               # CPU utilization string
├── memory[]
│   └── usage               # Memory utilization string
├── details[]
│   ├── model               # Hardware model (e.g. "M4350-44M4X4V")
│   ├── fwVer               # Firmware version
│   ├── sn                  # Serial number
│   └── upTime              # System uptime string
├── fan[]
│   └── details[]
│       ├── desc            # Fan name
│       ├── speed           # RPM
│       └── dutyLevel       # Duty cycle percentage
└── sensor[]
    └── details[]
        ├── desc            # Sensor name
        ├── temp            # Current temperature (C)
        └── maxTemp         # Maximum rated temperature (C)

GET /api/v1/swcfg_port

Returns the full port configuration for every port on the switch.

CLI command: netgear-cli ports

Response fields:

switchPortConfig
└── unit[]
    └── slot[]
        └── port[]
            ├── id              # Port number
            ├── portName        # Port label (e.g. "0/1")
            ├── portNum         # Port number (same as id)
            ├── adminState      # 1 = Enabled, 0 = Disabled
            ├── linkState       # 0 = Up, 1 = Down
            ├── linkSpeed       # Speed string (e.g. "1000")
            ├── switchportMode  # 0=Access, 1=Trunk, 2=General, 3=Hybrid
            ├── vlan[]          # Assigned VLAN IDs
            └── poeIsValid      # 3 = PoE capable, 0 = Not PoE

GET /api/v1/swcfg_ports_status?indexPage=1&pageSize=200

Returns port status including descriptions and profile assignments. Supports pagination.

CLI command: netgear-cli port-status

Query parameters:

Parameter Description
indexPage Page number (starts at 1)
pageSize Results per page (max 200)

Response fields:

switchPortStatus
└── rows[]
    ├── portNum             # Port number
    ├── portName            # Port label
    ├── portStr             # Port string identifier
    ├── description         # User-configured port description
    ├── profileName         # Assigned VLAN/network profile name
    ├── profileTemplate     # Profile template name
    ├── physicalStatus      # Physical link status string
    ├── speed               # Negotiated speed
    ├── stpMode             # Spanning Tree Protocol mode
    ├── flowControl         # Flow control setting
    └── frameSize           # Maximum frame size

GET /api/v1/neighbor?indexPage=1&pageSize=9999

Returns the combined LLDP and ARP neighbor table. This is where you find connected devices — their MAC addresses, IP addresses, and hostnames.

CLI command: netgear-cli neighbors

Query parameters:

Parameter Description
indexPage Page number (starts at 1)
pageSize Results per page

Response fields:

lldpRemoteDevice
└── rows[]
    ├── port                # Local port number
    ├── portNum             # Local port number (alternate field)
    ├── portName            # Local port label
    ├── hostMacAddress      # Remote device MAC address
    ├── hostIpAddress       # Remote device IP address
    ├── hostName            # Remote device hostname
    ├── source              # Discovery source: "lldp" or "arp"
    ├── remotePortId        # Remote port identifier
    └── systemDescription   # Remote device system description

GET /api/v1/swcfg_poe

Returns PoE status and configuration for every PoE-capable port.

CLI command: netgear-cli poe

Response fields:

poePortConfig[]
├── port                # Port number
├── enable              # 1 = Enabled, 0 = Disabled
├── status              # See status codes below
├── currentPower        # Current draw in tenths of watts (divide by 100 for W)
├── powerLimit          # Max power in tenths of watts (divide by 1000 for W)
├── classification      # PoE device class (0-8)
├── priority            # 0=Low, 1=Medium, 2=High, 3=Critical
├── powerMode           # See power mode codes below
└── schedule            # Schedule name or empty

PoE status codes:

Code Status
0 Disabled
1 Searching
2 Delivering
3 Fault
4 Test
5 Other Fault

PoE power mode codes:

Code Standard
1 PoE (802.3af) — up to 15.4W
2 PoE+ (802.3at) — up to 30W
3 PoE++ (802.3bt Type 3) — up to 60W
4 PoE++ (802.3bt Type 4) — up to 90W
7 Auto

GET /api/v1/sw_lag_cfg

Returns Link Aggregation Group (LAG / port channel) configuration.

CLI command: netgear-cli lag

Response fields:

switchConfigLagGroup[]
├── groupId         # LAG group ID
├── name            # LAG name
├── portNum         # LAG logical port number
├── lagMode         # 1=Static, 2=LACP Passive, 3=LACP Active
├── members[]       # Member port numbers
├── memberName[]    # Member port labels
├── balanceMode     # 1=Src/Dst MAC, 2=Src/Dst IP, 3=Src/Dst MAC+IP
├── vlanId          # VLAN ID
├── trunkMode       # Trunk mode enabled
└── staticMode      # Static mode enabled

GET /api/v1/profile/list

Returns VLAN/network profile definitions. Each profile maps to a VLAN ID and defines tagged/untagged port membership.

CLI command: netgear-cli vlans (fetches this along with port-status)

Response fields:

profileList[]
├── id                  # Profile ID
├── name                # Profile name
├── color               # Hex color code for UI display
├── profileType         # Profile type identifier
└── vlans[]
    ├── vlanId          # VLAN ID (e.g. 100)
    └── ports
    │   ├── tagged[]    # Ports carrying this VLAN tagged
    │   └── untagged[]  # Ports carrying this VLAN untagged
    └── routing
        ├── ipAddr      # SVI IP address
        └── ipMask      # SVI subnet mask

GET /api/v1/profile

Returns QoS profile templates — the building blocks that define DSCP mappings, multicast, STP, MTU, and other per-profile settings.

CLI command: netgear-cli qos (fetches this along with profile/list and port-status)

Response fields:

profile[]
├── name                # Profile name
├── description         # Profile description
├── color
│   └── value           # Hex color code
├── custom              # Is user-defined (boolean)
├── activation          # Is active (boolean)
├── PTP                 # Precision Time Protocol enabled
├── AVB                 # Audio Video Bridging enabled
├── MTU                 # Jumbo frame support
├── LLDP                # LLDP enabled
├── STP                 # Spanning Tree enabled
├── multicast
│   └── igmp            # IGMP snooping enabled
└── qos[]
    ├── type            # 1=DSCP mapping, 2=Scheduler
    ├── codePoint       # DSCP value (for type 1)
    ├── priority        # Queue priority
    └── schedulerType   # 1=Weighted, 2=Strict Priority

POST Endpoints

All POST endpoints require Session, Content-Type: application/json, and Accept: application/json headers.

POST /api/v1/device_name

Changes the switch hostname.

Request:

{
  "deviceName": "New Switch Name"
}

Response: Standard envelope with respCode: 0 on success.


POST /api/v1/swcfg_ports_ex

Updates port configuration — descriptions and profile/VLAN assignments.

Change port description:

{
  "switchPortStatus": {
    "rows": [
      {
        "portNum": 1,
        "description": "Lobby AP"
      }
    ]
  }
}

Change port VLAN/profile:

{
  "switchPortStatus": {
    "rows": [
      {
        "portNum": 1,
        "profileName": "VLAN100-Data"
      }
    ]
  }
}

Multiple ports can be updated in a single request by adding more objects to the rows array.

Response: Standard envelope with respCode: 0 on success.


POST /api/v1/swcfg_poe

Enables or disables PoE on specific ports.

Request:

{
  "poePortConfig": [
    {
      "port": "1",
      "enable": 1
    }
  ]
}
  • enable: 1 — Enable PoE
  • enable: 0 — Disable PoE

Multiple ports can be toggled in a single request by adding more objects to the array.

Response: Standard envelope with respCode: 0 on success.


POST /api/v1/arp_table_flush

Flushes the ARP table to clear stale neighbor entries. Useful when devices have moved ports or been disconnected.

Request:

{
  "flush": true
}

Response: Empty body (HTTP 200). The Rust CLI synthesizes a success response:

{
  "status": "ok",
  "httpStatus": 200,
  "endpoint": "/api/v1/arp_table_flush"
}

Rust CLI Reference

The CLI is the bridge between the Flask web server and the switch. It handles authentication, makes the API call, and outputs JSON to stdout.

Commands

# Test connectivity and credentials
netgear-cli login -H 10.0.0.1 -u admin -p password

# Read operations
netgear-cli device-info -H 10.0.0.1 -u admin -p password
netgear-cli ports       -H 10.0.0.1 -u admin -p password
netgear-cli port-status -H 10.0.0.1 -u admin -p password
netgear-cli neighbors   -H 10.0.0.1 -u admin -p password
netgear-cli poe         -H 10.0.0.1 -u admin -p password
netgear-cli lag         -H 10.0.0.1 -u admin -p password
netgear-cli vlans       -H 10.0.0.1 -u admin -p password
netgear-cli qos         -H 10.0.0.1 -u admin -p password
netgear-cli config      -H 10.0.0.1 -u admin -p password   # all of the above combined

# Write operations (POST any endpoint with arbitrary JSON)
netgear-cli set -H 10.0.0.1 -u admin -p password \
  --endpoint /api/v1/swcfg_poe \
  --data '{"poePortConfig":[{"port":"1","enable":1}]}'

CLI Command to Switch Endpoint Mapping

CLI Command Switch Endpoint Method
login /api/v1/login POST
device-info /api/v1/device_info GET
ports /api/v1/swcfg_port GET
port-status /api/v1/swcfg_ports_status?indexPage=1&pageSize=200 GET
neighbors /api/v1/neighbor?indexPage=1&pageSize=9999 GET
poe /api/v1/swcfg_poe GET
lag /api/v1/sw_lag_cfg GET
vlans /api/v1/profile/list + /api/v1/swcfg_ports_status GET
qos /api/v1/profile + /api/v1/profile/list + /api/v1/swcfg_ports_status GET
config All GET endpoints combined GET
set Any endpoint (user-specified) POST

Output Format

  • Success: JSON on stdout, exit code 0
  • Error: JSON on stderr ({"error": "message"}), exit code 1

Flask Web API

These are the internal endpoints served by the Python backend. The browser talks to these; they invoke the Rust CLI under the hood.

GET /api/switches

Returns the switch inventory from config/switches.json.

POST /api/switches

Adds a new switch. Body: {"host": "10.0.0.1", "name": "Lobby Switch"}. Auto-assigns an ID.

DELETE /api/switches/<switch_id>

Removes a switch from the inventory.

POST /api/switches/<switch_id>/<action>

Runs a CLI read command against a switch. Valid actions: login, device-info, ports, port-status, neighbors, poe, lag, config, qos, vlans.

POST /api/switches/<switch_id>/set

Pushes a configuration change to a switch. Logs the change to pushlog.json.

Request:

{
  "endpoint": "/api/v1/swcfg_poe",
  "data": {"poePortConfig": [{"port": "1", "enable": 1}]},
  "old_value": {"poePortConfig": [{"port": "1", "enable": 0}]}
}

The old_value is stored in the push log for audit purposes but is not sent to the switch.

GET /api/pushlog

Returns the audit log of all configuration changes.

Response:

{
  "entries": [
    {
      "timestamp": "2026-06-15T11:34:14.311592",
      "switch_id": "switch-1",
      "switch_host": "10.201.100.164",
      "endpoint": "/api/v1/swcfg_poe",
      "old_value": {"poePortConfig": [{"port": "1", "enable": 0}]},
      "new_value": {"poePortConfig": [{"port": "1", "enable": 1}]},
      "result": "success"
    }
  ]
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages