From 00c677fec09394bc06bbedef834609869ebaee3a Mon Sep 17 00:00:00 2001 From: i521907 Date: Fri, 13 Mar 2026 15:05:56 -0400 Subject: [PATCH 1/2] fix: delete PIM rp-items before multicast group replace --- internal/provider/cisco/nxos/provider.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go index e2e176cd..f20329f0 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -1685,6 +1685,17 @@ func (p *Provider) EnsurePIM(ctx context.Context, req *provider.EnsurePIMRequest del := make([]gnmiext.Configurable, 0, 3) if len(rpItems.StaticRPList) > 0 { + // NX-OS fails with "child (Rn) cannot be added to deleted object" when a + // gNMI Replace tries to update nested rpgrplist-items within a StaticRP + // in the same transaction (e.g. when multicastGroups change). Work around + // this by deleting the existing StaticRPItems first when the configuration + // has changed, so the Replace only needs to add entries. + currentRPItems := new(StaticRPItems) + if err := p.client.GetConfig(ctx, currentRPItems); err == nil && !reflect.DeepEqual(rpItems, currentRPItems) { + if err := p.client.Delete(ctx, new(StaticRPItems)); err != nil { + return err + } + } conf = append(conf, rpItems) } else { del = append(del, rpItems) From a8dbda009f4166652d81adab2eb8ada8db1b0a39 Mon Sep 17 00:00:00 2001 From: i521907 Date: Thu, 26 Mar 2026 06:27:24 -0400 Subject: [PATCH 2/2] fix: surgical per-RP update/delete --- internal/provider/cisco/nxos/pim.go | 1 + internal/provider/cisco/nxos/provider.go | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/internal/provider/cisco/nxos/pim.go b/internal/provider/cisco/nxos/pim.go index 49c3191f..5b85ed7f 100644 --- a/internal/provider/cisco/nxos/pim.go +++ b/internal/provider/cisco/nxos/pim.go @@ -9,6 +9,7 @@ var ( _ gnmiext.Configurable = (*PIM)(nil) _ gnmiext.Configurable = (*PIMDom)(nil) _ gnmiext.Configurable = (*StaticRPItems)(nil) + _ gnmiext.Configurable = (*StaticRP)(nil) _ gnmiext.Configurable = (*AnycastPeerItems)(nil) _ gnmiext.Configurable = (*PIMIfItems)(nil) ) diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go index f20329f0..b97db842 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -1685,18 +1685,20 @@ func (p *Provider) EnsurePIM(ctx context.Context, req *provider.EnsurePIMRequest del := make([]gnmiext.Configurable, 0, 3) if len(rpItems.StaticRPList) > 0 { - // NX-OS fails with "child (Rn) cannot be added to deleted object" when a - // gNMI Replace tries to update nested rpgrplist-items within a StaticRP - // in the same transaction (e.g. when multicastGroups change). Work around - // this by deleting the existing StaticRPItems first when the configuration - // has changed, so the Replace only needs to add entries. - currentRPItems := new(StaticRPItems) - if err := p.client.GetConfig(ctx, currentRPItems); err == nil && !reflect.DeepEqual(rpItems, currentRPItems) { - if err := p.client.Delete(ctx, new(StaticRPItems)); err != nil { - return err + current := new(StaticRPItems) + if err := p.client.GetConfig(ctx, current); err != nil && !errors.Is(err, gnmiext.ErrNil) { + return err + } + for _, rp := range rpItems.StaticRPList { + if got, ok := current.StaticRPList.Get(rp.Key()); !ok || !reflect.DeepEqual(got, rp) { + conf = append(conf, rp) + } + } + for _, rp := range current.StaticRPList { + if _, ok := rpItems.StaticRPList.Get(rp.Key()); !ok { + del = append(del, rp) } } - conf = append(conf, rpItems) } else { del = append(del, rpItems) }