diff --git a/PROJECT b/PROJECT
index c5a6f881d..f67d9f710 100644
--- a/PROJECT
+++ b/PROJECT
@@ -285,6 +285,14 @@ resources:
kind: BGPConfig
path: github.com/ironcore-dev/network-operator/api/cisco/nx/v1alpha1
version: v1alpha1
+- api:
+ crdVersion: v1
+ namespaced: true
+ domain: cisco.networking.metal.ironcore.dev
+ group: nx
+ kind: PIMConfig
+ path: github.com/ironcore-dev/network-operator/api/cisco/nx/v1alpha1
+ version: v1alpha1
- api:
crdVersion: v1
namespaced: true
diff --git a/api/cisco/nx/v1alpha1/pim_types.go b/api/cisco/nx/v1alpha1/pim_types.go
new file mode 100644
index 000000000..074fd4484
--- /dev/null
+++ b/api/cisco/nx/v1alpha1/pim_types.go
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
+// SPDX-License-Identifier: Apache-2.0
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+
+ v1alpha1 "github.com/ironcore-dev/network-operator/api/core/v1alpha1"
+)
+
+// +kubebuilder:rbac:groups=nx.cisco.networking.metal.ironcore.dev,resources=pimconfigs,verbs=get;list;watch
+
+// PIMConfigSpec defines the Cisco NX-OS specific PIM configuration.
+type PIMConfigSpec struct {
+ // LogNeighborChanges enables logging when a PIM neighbor is added or removed.
+ // +optional
+ LogNeighborChanges *bool `json:"logNeighborChanges,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:path=pimconfigs
+// +kubebuilder:resource:singular=pimconfig
+
+// PIMConfig is the Schema for the PIMConfig API
+type PIMConfig struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty,omitzero"`
+
+ // spec defines the desired state of PIMConfig
+ // +required
+ Spec PIMConfigSpec `json:"spec"`
+}
+
+// +kubebuilder:object:root=true
+
+// PIMConfigList contains a list of PIMConfigs
+type PIMConfigList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []PIMConfig `json:"items"`
+}
+
+// init registers the PIMConfig type with the scheme and sets
+// itself as a dependency for the PIM core type.
+func init() {
+ v1alpha1.RegisterPIMDependency(GroupVersion.WithKind("PIMConfig"))
+ SchemeBuilder.Register(func(s *runtime.Scheme) error {
+ s.AddKnownTypes(GroupVersion, &PIMConfig{}, &PIMConfigList{})
+ return nil
+ })
+}
diff --git a/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go b/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go
index 1475968f6..12224806f 100644
--- a/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go
+++ b/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go
@@ -845,6 +845,84 @@ func (in *NetworkVirtualizationEdgeConfigSpec) DeepCopy() *NetworkVirtualization
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PIMConfig) DeepCopyInto(out *PIMConfig) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PIMConfig.
+func (in *PIMConfig) DeepCopy() *PIMConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(PIMConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PIMConfig) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PIMConfigList) DeepCopyInto(out *PIMConfigList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]PIMConfig, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PIMConfigList.
+func (in *PIMConfigList) DeepCopy() *PIMConfigList {
+ if in == nil {
+ return nil
+ }
+ out := new(PIMConfigList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PIMConfigList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PIMConfigSpec) DeepCopyInto(out *PIMConfigSpec) {
+ *out = *in
+ if in.LogNeighborChanges != nil {
+ in, out := &in.LogNeighborChanges, &out.LogNeighborChanges
+ *out = new(bool)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PIMConfigSpec.
+func (in *PIMConfigSpec) DeepCopy() *PIMConfigSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(PIMConfigSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Peer) DeepCopyInto(out *Peer) {
*out = *in
diff --git a/charts/network-operator/templates/rbac/manager-role.yaml b/charts/network-operator/templates/rbac/manager-role.yaml
index 79729cf5f..8c4b0acd6 100644
--- a/charts/network-operator/templates/rbac/manager-role.yaml
+++ b/charts/network-operator/templates/rbac/manager-role.yaml
@@ -185,6 +185,7 @@ rules:
- lldpconfigs
- managementaccessconfigs
- networkvirtualizationedgeconfigs
+ - pimconfigs
verbs:
- get
- list
diff --git a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_pimconfigs.yaml b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_pimconfigs.yaml
new file mode 100644
index 000000000..763951fbc
--- /dev/null
+++ b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_pimconfigs.yaml
@@ -0,0 +1,51 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.22.0
+ name: pimconfigs.nx.cisco.networking.metal.ironcore.dev
+spec:
+ group: nx.cisco.networking.metal.ironcore.dev
+ names:
+ kind: PIMConfig
+ listKind: PIMConfigList
+ plural: pimconfigs
+ singular: pimconfig
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: PIMConfig is the Schema for the PIMConfig API
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: spec defines the desired state of PIMConfig
+ properties:
+ logNeighborChanges:
+ description: LogNeighborChanges enables logging when a PIM neighbor
+ is added or removed.
+ type: boolean
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 66cdebb60..3ce8c6af7 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -179,6 +179,7 @@ rules:
- lldpconfigs
- managementaccessconfigs
- networkvirtualizationedgeconfigs
+ - pimconfigs
verbs:
- get
- list
diff --git a/config/samples/cisco/nx/v1alpha1_pimconfig.yaml b/config/samples/cisco/nx/v1alpha1_pimconfig.yaml
new file mode 100644
index 000000000..bb84f8182
--- /dev/null
+++ b/config/samples/cisco/nx/v1alpha1_pimconfig.yaml
@@ -0,0 +1,9 @@
+apiVersion: nx.cisco.networking.metal.ironcore.dev/v1alpha1
+kind: PIMConfig
+metadata:
+ labels:
+ app.kubernetes.io/name: network-operator
+ app.kubernetes.io/managed-by: kustomize
+ name: leaf1-pimconfig
+spec:
+ logNeighborChanges: true
diff --git a/config/samples/v1alpha1_pim.yaml b/config/samples/v1alpha1_pim.yaml
index bbcf2ad57..6a2af0ac9 100644
--- a/config/samples/v1alpha1_pim.yaml
+++ b/config/samples/v1alpha1_pim.yaml
@@ -9,6 +9,12 @@ metadata:
spec:
deviceRef:
name: leaf1
+ # Uncomment to add NXOS provider-specific config
+ # See: ./cisco/nx/v1alpha1_pimconfig.yaml
+ # providerConfigRef:
+ # apiVersion: nx.cisco.networking.metal.ironcore.dev/v1alpha1
+ # kind: PIMConfig
+ # name: leaf1-pimconfig
rendezvousPoints:
- address: 10.0.0.100
multicastGroups:
diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md
index fef20cbf9..2e5b7eb22 100644
--- a/docs/api-reference/index.md
+++ b/docs/api-reference/index.md
@@ -4577,6 +4577,7 @@ Package v1alpha1 contains API Schema definitions for the nx.cisco.networking.met
- [LLDPConfig](#lldpconfig)
- [ManagementAccessConfig](#managementaccessconfig)
- [NetworkVirtualizationEdgeConfig](#networkvirtualizationedgeconfig)
+- [PIMConfig](#pimconfig)
- [System](#system)
- [VPCDomain](#vpcdomain)
@@ -5097,6 +5098,40 @@ _Appears in:_
| `infraVLANs` _[VLANListItem](#vlanlistitem) array_ | InfraVLANs specifies VLANs used by all SVI interfaces for uplink and vPC peer-links in VXLAN as infra-VLANs.
Valid VLAN IDs are 1-4092 because NX-OS always reserves VLANs 4093-4095 for internal use.
The provider also checks these VLANs against the device's configurable internal reserved VLAN range.
The total number of VLANs configured must not exceed 512.
Elements in the list must not overlap with each other. | | MaxItems: 10
Optional: \{\}
|
+#### PIMConfig
+
+
+
+PIMConfig is the Schema for the PIMConfig API
+
+
+
+
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `apiVersion` _string_ | `nx.cisco.networking.metal.ironcore.dev/v1alpha1` | | |
+| `kind` _string_ | `PIMConfig` | | |
+| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
+| `spec` _[PIMConfigSpec](#pimconfigspec)_ | spec defines the desired state of PIMConfig | | Required: \{\}
|
+
+
+#### PIMConfigSpec
+
+
+
+PIMConfigSpec defines the Cisco NX-OS specific PIM configuration.
+
+
+
+_Appears in:_
+- [PIMConfig](#pimconfig)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `logNeighborChanges` _boolean_ | LogNeighborChanges enables logging when a PIM neighbor is added or removed. | | Optional: \{\}
|
+
+
#### Peer
diff --git a/internal/provider/cisco/nxos/pim.go b/internal/provider/cisco/nxos/pim.go
index c484ada9c..80148c7e4 100644
--- a/internal/provider/cisco/nxos/pim.go
+++ b/internal/provider/cisco/nxos/pim.go
@@ -27,8 +27,9 @@ func (*PIM) XPath() string {
}
type PIMDom struct {
- Name string `json:"name"`
- AdminSt AdminSt `json:"adminSt"`
+ Name string `json:"name"`
+ AdminSt AdminSt `json:"adminSt"`
+ LogNbhChng *bool `json:"logNbhChng,omitempty"`
}
func (*PIMDom) IsListItem() {}
diff --git a/internal/provider/cisco/nxos/pim_test.go b/internal/provider/cisco/nxos/pim_test.go
index 6a5430b9b..53908497c 100644
--- a/internal/provider/cisco/nxos/pim_test.go
+++ b/internal/provider/cisco/nxos/pim_test.go
@@ -15,4 +15,11 @@ func init() {
rp := &StaticRP{Addr: "10.0.0.100/32"}
rp.RpgrplistItems.RPGrpListList.Set(&StaticRPGrp{GrpListName: "224.0.0.0/4"})
Register("pim_rp", rp)
+
+ logEnabled := true
+ Register("pim_dom_log", &PIMDom{
+ Name: "default",
+ AdminSt: AdminStEnabled,
+ LogNbhChng: &logEnabled,
+ })
}
diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go
index 4001c4aa4..a1c63df2a 100644
--- a/internal/provider/cisco/nxos/provider.go
+++ b/internal/provider/cisco/nxos/provider.go
@@ -2429,6 +2429,13 @@ func (p *Provider) EnsurePIM(ctx context.Context, req *provider.EnsurePIMRequest
f.AdminSt = AdminStEnabled
sb.Update(f)
+ var cfg nxv1alpha1.PIMConfig
+ if req.ProviderConfig != nil {
+ if err := req.ProviderConfig.Into(&cfg); err != nil {
+ return err
+ }
+ }
+
pim := new(PIM)
pim.AdminSt = AdminStEnabled
pim.InstItems.AdminSt = AdminStEnabled
@@ -2444,6 +2451,7 @@ func (p *Provider) EnsurePIM(ctx context.Context, req *provider.EnsurePIMRequest
if req.PIM.Spec.AdminState == v1alpha1.AdminStateDown {
dom.AdminSt = AdminStDisabled
}
+ dom.LogNbhChng = cfg.Spec.LogNeighborChanges
sb.Patch(dom)
rpItems := new(StaticRPItems)
diff --git a/internal/provider/cisco/nxos/testdata/pim_dom_log.json b/internal/provider/cisco/nxos/testdata/pim_dom_log.json
new file mode 100644
index 000000000..19897345f
--- /dev/null
+++ b/internal/provider/cisco/nxos/testdata/pim_dom_log.json
@@ -0,0 +1,15 @@
+{
+ "pim-items": {
+ "inst-items": {
+ "dom-items": {
+ "Dom-list": [
+ {
+ "name": "default",
+ "adminSt": "enabled",
+ "logNbhChng": true
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/internal/provider/cisco/nxos/testdata/pim_dom_log.json.txt b/internal/provider/cisco/nxos/testdata/pim_dom_log.json.txt
new file mode 100644
index 000000000..6e73f1ae5
--- /dev/null
+++ b/internal/provider/cisco/nxos/testdata/pim_dom_log.json.txt
@@ -0,0 +1 @@
+ip pim log-neighbor-changes
diff --git a/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar
index 2ada9693f..170c6c4ac 100644
--- a/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar
+++ b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar
@@ -14,6 +14,15 @@ spec:
addresses:
- 10.255.255.20/32
+-- pimconfigs/log-neighbor --
+apiVersion: nx.cisco.networking.metal.ironcore.dev/v1alpha1
+kind: PIMConfig
+metadata:
+ name: log-neighbor
+ namespace: default
+spec:
+ logNeighborChanges: true
+
-- pims/multicast --
apiVersion: networking.metal.ironcore.dev/v1alpha1
kind: PIM
@@ -24,6 +33,10 @@ spec:
deviceRef:
name: device
adminState: Up
+ providerConfigRef:
+ apiVersion: nx.cisco.networking.metal.ironcore.dev/v1alpha1
+ kind: PIMConfig
+ name: log-neighbor
interfaceRefs:
- name: lo-pim
mode: Sparse
@@ -119,6 +132,7 @@ spec:
{
"name": "default",
"adminSt": "enabled",
+ "logNbhChng": true,
"if-items": {
"If-list": [
{