This document describes the high-level architecture of nmstate-console-plugin, an OpenShift console dynamic plugin for kubernetes-nmstate.
┌─────────────────────────────────────────────────┐
│ OpenShift Console │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Console Plugin Loader │ │
│ │ (loads remote modules at runtime) │ │
│ └──────────────┬─────────────────────────────┘ │
│ │ loads │
│ ┌──────────────▼─────────────────────────────┐ │
│ │ nmstate-console-plugin │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │
│ │ │ Policies │ │ States │ │ Phys. Net. │ │ │
│ │ └────┬─────┘ └────┬─────┘ └─────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ ┌────▼─────────────▼─────────────▼──────┐ │ │
│ │ │ OpenShift Dynamic Plugin SDK │ │ │
│ │ │ (useK8sWatchResource, k8sCreate...) │ │ │
│ │ └───────────────────┬───────────────────┘ │ │
│ └──────────────────────┼─────────────────────┘ │
│ │ │
└─────────────────────────┼────────────────────────┘
│ K8s API
┌───────────▼───────────┐
│ kubernetes-nmstate │
│ (NMState Operator) │
└───────────────────────┘
The plugin runs as a remote webpack module loaded by the OpenShift console at runtime. It never calls the Kubernetes API directly — all cluster communication goes through the Dynamic Plugin SDK, which proxies requests through the console backend.
The plugin registers itself with the console via plugin-manifest.ts, which aggregates extensions from each view's manifest.ts:
plugin-manifest.ts
├── pluginMetadata (name, version, exposedModules)
└── extensions[] (nav items, pages, templates, feature flags)
├── PolicyExtensions → policies/manifest.ts
├── StateExtensions → states/manifest.ts
├── NodeNetworkConfigurationExtensions → nodenetworkconfiguration/manifest.ts
├── PhysicalNetworksExtensions → physical-networks/manifest.ts
└── console.flag → utils/flags/ (NMSTATE_DYNAMIC)
Each manifest exports:
- ExposedModules — maps module names to file paths for webpack code splitting
- Extensions — typed
EncodedExtensionobjects that register console navigation items, resource list/detail pages, YAML templates, and route pages
Each view is a self-contained feature directory under src/views/. The structure below is a representative example — not every view will have all of these subdirectories:
src/views/{view}/
├── manifest.ts # Plugin extension registration
├── constants.ts # View-specific constants
├── utils.ts # View-specific utilities
├── list/ # List page component
├── details/ # Details page component
├── new/ # Create/edit form (policies only)
├── actions/ # Action menu items (policies only)
├── components/ # View-specific UI components
└── hooks/ # View-specific React hooks
| View | K8s Resource | Extension Type | Perspectives |
|---|---|---|---|
| policies | NodeNetworkConfigurationPolicy |
Resource list + details + YAML template | admin, virtualization |
| states | NodeNetworkState |
Resource list | admin |
| physical-networks | (aggregated from enactments + states) | Href route page | admin, virtualization |
| nodenetworkconfiguration | (aggregated from nodes + states + enactments) | Href route page | admin, virtualization |
Resource-based views (policies, states) use console.page/resource/list and console.page/resource/details extensions, which automatically bind to the K8s model and provide standard console features (filtering, pagination, YAML editor).
Route-based views (physical-networks, nodenetworkconfiguration) use console.page/route and console.navigation/href extensions, rendering fully custom pages with their own data fetching.
src/utils/
├── components/ # Reusable UI: Loading spinner, HelpTextIcon, etc.
├── hooks/ # useNMStateTranslation, shared data hooks
├── flags/ # Feature flag: NMSTATE_DYNAMIC (set unconditionally on plugin load)
├── resources/ # K8s resource helpers organized by resource type:
│ ├── policies/ # getters.ts, selectors.ts, utils.ts
│ ├── nns/ # getters.tsx, utils.ts
│ ├── enactments/
│ ├── interfaces/
│ ├── neighbors/
│ └── ovn/
├── telemetry/ # Usage tracking
├── constants.ts # Global constants
└── helpers.ts # General utility functions (isEmpty, etc.)
Resource utilities follow a consistent pattern:
- selectors (
selectors.ts) — extract and transform fields from K8s resource objects - getters (
getters.ts) — older naming convention for the same purpose; some resource directories still usegetters.ts(e.g.,nns/,interfaces/) while others have migrated toselectors.ts(e.g.,enactments/). New code should use theselectorsnaming - utils (
utils.ts) — transform or compute derived data
src/console-models/ # K8sModel definitions (used by SDK)
├── NMStateModel.ts # NMState (nmstate.io/v1)
├── NodeNetworkConfigurationPolicyModel.ts
├── NodeNetworkConfigurationEnactmentModel.ts
├── NodeNetworkStateModel.ts
├── NodeModel.ts # Core v1 Node
├── ClusterUserDefinedNetworkModel.ts
├── modelUtils.ts # modelToGroupVersionKind, modelToRef helpers
└── index.ts # Re-exports for @models alias
src/nmstate-types/ # NMState CRD schemas and custom types
├── crds/ # Raw CRD YAML files (downloaded via script)
├── custom-models/ # Extended TypeScript type definitions
└── download-nmstate-crds.sh # Script to refresh CRDs from upstream
Models serve two purposes:
- SDK integration —
K8sModelobjects tell the console how to watch, list, and navigate to resources - Type safety — NMState API types from
@kubevirt-ui/kubevirt-api/nmstateprovide TypeScript interfaces for resource objects
K8s API (NMState CRDs)
│
▼
useK8sWatchResource(model) ← SDK hook, real-time watch
│
▼
Resource selectors ← src/utils/resources/
│
▼
View component ← src/views/{view}/
│
▼
PatternFly UI ← tables, forms, topology
- Watch — view components call
useK8sWatchResourcewith aK8sModelto establish a real-time watch on cluster resources - Transform — resource selectors extract and reshape the raw K8s objects for display
- Render — PatternFly components render the data as tables, forms, detail pages, or topology graphs
- Mutate — create/edit/delete operations use SDK functions (
k8sCreate,k8sPatch,k8sDelete) which proxy through the console backend
The topology view (nodenetworkconfiguration) aggregates data from three resource types — Nodes, NodeNetworkStates, and NodeNetworkConfigurationEnactments — and renders them using PatternFly Topology.
The plugin sets a single feature flag on load:
NMSTATE_DYNAMIC— set totrueunconditionally viaenableNMStateDynamicFlag. Used by physical-networks and nodenetworkconfiguration extensions to gate their nav items (these views require the plugin to be loaded).
Source (TypeScript/React)
│
▼ webpack + ConsoleRemotePlugin
Remote webpack module (dist/)
│
▼ Container image (Dockerfile)
quay.io/nmstate/nmstate-console-plugin
│
▼ Helm chart or OC template
OpenShift Deployment + Service + ConsolePlugin CR
│
▼ Console operator patches
OpenShift Console loads plugin at runtime
- Webpack builds the plugin using
ConsoleRemotePluginfrom the SDK, which generates the remote module entry points and plugin metadata - Deployment creates a Kubernetes
Deployment+Serviceserving the static assets, plus aConsolePlugincustom resource that registers the plugin with the console operator - The console operator must be patched to include
nmstate-console-pluginin its plugin list
The repo maintains multiple active release branches aligned to OpenShift versions:
main— targets the next OpenShift releaserelease-X.Y— stable branches for OpenShift X.Y (currentlyrelease-4.17throughrelease-4.23)
Each release branch pins its SDK version to match (e.g., release-4.22 uses @openshift-console/dynamic-plugin-sdk 4.22.x). Bug fixes and CVE remediations land on main first, then are cherry-picked to affected release branches as separate PRs.
| Package | Purpose |
|---|---|
@openshift-console/dynamic-plugin-sdk |
Console integration: K8s watches, navigation, pages |
@openshift-console/dynamic-plugin-sdk-webpack |
Webpack plugin for building console remote modules |
@patternfly/react-core |
UI components (tables, forms, modals, alerts) |
@patternfly/react-topology |
Topology graph visualization |
@kubevirt-ui/kubevirt-api |
TypeScript types for NMState and K8s resources |
react-i18next |
Internationalization framework |