Remove SpeakerIdMapping 17/98617/9
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 19 Nov 2021 15:05:48 +0000 (16:05 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 20 Nov 2021 01:07:50 +0000 (02:07 +0100)
Tunnelling this simple map through PCEPTopologyConfiguration is
completely unnecessary as PCEPStatefulPeerProposal can easily have
it pushed via a DTCL and keep it close by.

JIRA: BGPCEP-983
Change-Id: I76abc4e94f3f69c4fab00b39dbaf0cfd6b11e055
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/PCEPStatefulPeerProposal.java
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/PCEPTopologyConfiguration.java
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/PCEPTopologyProvider.java
pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/SpeakerIdMapping.java [deleted file]
pcep/topology/topology-provider/src/test/java/org/opendaylight/bgpcep/pcep/topology/provider/PCEPStatefulPeerProposalTest.java

index 1c16c29ca0ee1267f88d25c2b3c5669022a5d46a..8c1a72f7f2a9fb7e2a64507aa1622d755dafa33e 100644 (file)
@@ -7,13 +7,20 @@
  */
 package org.opendaylight.bgpcep.pcep.topology.provider;
 
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
+import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.protocol.pcep.PCEPPeerProposal;
@@ -22,35 +29,62 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.Tlvs3Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.lsp.db.version.tlv.LspDbVersion;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.speaker.entity.id.tlv.SpeakerEntityIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.config.rev200120.pcep.node.config.SessionConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Tlvs1;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.open.object.open.TlvsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.config.rev181109.PcepNodeConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.Node1;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.PathComputationClient;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.sync.optimizations.config.rev181109.PcepNodeSyncConfig;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class PCEPStatefulPeerProposal implements PCEPPeerProposal {
+final class PCEPStatefulPeerProposal extends AbstractRegistration implements PCEPPeerProposal {
+    private static final class SpeakerIdListener implements ClusteredDataTreeChangeListener<PcepNodeSyncConfig> {
+        final Map<NodeId, byte[]> map = new ConcurrentHashMap<>();
+        final Registration reg;
+
+        SpeakerIdListener(final DataBroker dataBroker, final InstanceIdentifier<Topology> topologyId) {
+            reg = dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(
+                LogicalDatastoreType.CONFIGURATION, topologyId.child(Node.class).augmentation(PcepNodeConfig.class)
+                    .child(SessionConfig.class).augmentation(PcepNodeSyncConfig.class)), this);
+        }
+
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeModification<PcepNodeSyncConfig>> changes) {
+            for (var change : changes) {
+                final var nodeId = verifyNotNull(change.getRootPath().getRootIdentifier().firstKeyOf(Node.class))
+                    .getNodeId();
+                final var config = change.getRootNode().getDataAfter();
+                if (config != null) {
+                    final var speakerEntityId = config.getSpeakerEntityIdValue();
+                    if (speakerEntityId != null) {
+                        map.put(nodeId, speakerEntityId);
+                        continue;
+                    }
+                }
+                map.remove(nodeId);
+            }
+        }
+    }
+
     private static final Logger LOG = LoggerFactory.getLogger(PCEPStatefulPeerProposal.class);
 
     private final InstanceIdentifier<Topology> topologyId;
+    private final SpeakerIdListener speakerIds;
     private final DataBroker dataBroker;
 
-    private volatile SpeakerIdMapping speakerIds;
-
-    PCEPStatefulPeerProposal(final DataBroker dataBroker, final InstanceIdentifier<Topology> topologyId,
-            final SpeakerIdMapping speakerIds) {
+    PCEPStatefulPeerProposal(final DataBroker dataBroker, final InstanceIdentifier<Topology> topologyId) {
         this.dataBroker = requireNonNull(dataBroker);
         this.topologyId = requireNonNull(topologyId);
-        // FIXME: BGPCEP-983: we should acquire this through DTCL
-        this.speakerIds = requireNonNull(speakerIds);
-    }
-
-    void setSpeakerIds(final SpeakerIdMapping speakerIds) {
-        this.speakerIds = requireNonNull(speakerIds);
+        speakerIds = new SpeakerIdListener(dataBroker, topologyId);
     }
 
     @Override
@@ -65,25 +99,24 @@ final class PCEPStatefulPeerProposal implements PCEPPeerProposal {
             return;
         }
 
-        // FIXME: BGPCEP-989: acquire this information via a DTCL and perform a simple lookup only
         final var addr = address.getAddress();
+        final var nodeId = ServerSessionManager.createNodeId(addr);
+        // FIXME: BGPCEP-989: acquire this information via a DTCL and perform a simple lookup only
         Optional<LspDbVersion> result = Optional.empty();
-
         try (ReadTransaction rTx = dataBroker.newReadOnlyTransaction()) {
             // FIXME: we should be listening for this configuration and keep a proper cache
             final ListenableFuture<Optional<LspDbVersion>> future = rTx.read(LogicalDatastoreType.OPERATIONAL,
-                topologyId.child(Node.class, new NodeKey(ServerSessionManager.createNodeId(addr)))
+                topologyId.child(Node.class, new NodeKey(nodeId))
                     .augmentation(Node1.class).child(PathComputationClient.class)
                     .augmentation(PathComputationClient1.class).child(LspDbVersion.class));
             try {
                 result = future.get();
             } catch (final InterruptedException | ExecutionException e) {
                 LOG.warn("Failed to read toplogy {}.", InstanceIdentifier.keyOf(topologyId), e);
-
             }
         }
 
-        final var speakerId = speakerIds.speakerIdForAddress(addr);
+        final var speakerId = speakerIds.map.get(nodeId);
         if (speakerId == null && !result.isPresent()) {
             return;
         }
@@ -97,4 +130,9 @@ final class PCEPStatefulPeerProposal implements PCEPPeerProposal {
         }
         openBuilder.addAugmentation(syncBuilder.build()).build();
     }
+
+    @Override
+    protected void removeRegistration() {
+        speakerIds.reg.close();
+    }
 }
index 1972bce61638c320db2bc71c5f913787440cafc4..f687b5456fd3411891ce5de11c9daa7c3bd028b8 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.bgpcep.pcep.topology.provider;
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.collect.ImmutableMap;
 import com.google.common.net.InetAddresses;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -23,23 +22,20 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.config.rev181109.PcepNodeConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.config.rev181109.PcepTopologyTypeConfig;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.sync.optimizations.config.rev181109.PcepNodeSyncConfig;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
 import org.opendaylight.yangtools.concepts.Immutable;
 
 final class PCEPTopologyConfiguration implements Immutable {
-    private final @NonNull SpeakerIdMapping speakerIds;
     private final @NonNull InetSocketAddress address;
     private final @NonNull KeyMapping keys;
     private final short rpcTimeout;
 
     PCEPTopologyConfiguration(final @NonNull InetSocketAddress address, final short rpcTimeout,
-            final @NonNull KeyMapping keys, final @NonNull SpeakerIdMapping speakerIds) {
+            final @NonNull KeyMapping keys) {
         this.address = requireNonNull(address);
         this.keys = requireNonNull(keys);
-        this.speakerIds = requireNonNull(speakerIds);
         this.rpcTimeout = rpcTimeout;
     }
 
@@ -56,7 +52,7 @@ final class PCEPTopologyConfiguration implements Immutable {
 
         return new PCEPTopologyConfiguration(
             getInetSocketAddress(sessionConfig.getListenAddress(), sessionConfig.getListenPort()),
-            sessionConfig.getRpcTimeout(), constructKeys(topology.getNode()), contructSpeakersId(topology.getNode()));
+            sessionConfig.getRpcTimeout(), constructKeys(topology.getNode()));
     }
 
     short getRpcTimeout() {
@@ -71,10 +67,6 @@ final class PCEPTopologyConfiguration implements Immutable {
         return keys;
     }
 
-    @NonNull SpeakerIdMapping getSpeakerIds() {
-        return speakerIds;
-    }
-
     private static @NonNull KeyMapping constructKeys(final @Nullable Map<NodeKey, Node> nodes) {
         if (nodes == null) {
             return KeyMapping.of();
@@ -102,33 +94,6 @@ final class PCEPTopologyConfiguration implements Immutable {
         return KeyMapping.of(passwords);
     }
 
-    private static @NonNull SpeakerIdMapping contructSpeakersId(final @Nullable Map<NodeKey, Node> nodes) {
-        if (nodes == null) {
-            return SpeakerIdMapping.of();
-        }
-
-        final var builder = ImmutableMap.<InetAddress, byte[]>builder();
-        for (var node : nodes.values()) {
-            if (node != null) {
-                final var nodeConfig = node.augmentation(PcepNodeConfig.class);
-                if (nodeConfig != null) {
-                    final var sessionConfig = nodeConfig.getSessionConfig();
-                    if (sessionConfig != null) {
-                        final var nodeSyncConfig = sessionConfig.augmentation(PcepNodeSyncConfig.class);
-                        if (nodeSyncConfig != null) {
-                            final var speakerEntityId = nodeSyncConfig.getSpeakerEntityIdValue();
-                            if (speakerEntityId != null) {
-                                builder.put(nodeAddress(node), speakerEntityId);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return SpeakerIdMapping.copyOf(builder.build());
-    }
-
     private static InetAddress nodeAddress(final Node node) {
         return InetAddresses.forString(node.getNodeId().getValue());
     }
index df4bbbb6aa61c36345971dc44c25a0824439a099..379d55baa453f539e61bc4d1858f9acbe3d514dd 100644 (file)
@@ -151,7 +151,6 @@ final class PCEPTopologyProvider extends DefaultTopologyReference {
             .filter(nodeId -> !Arrays.equals(currentKeys.get(nodeId), newKeys.get(nodeId)))
             .collect(Collectors.toUnmodifiableList());
 
-        proposal.setSpeakerIds(newConfiguration.getSpeakerIds());
         manager.setRpcTimeout(newConfiguration.getRpcTimeout());
         if (!outdatedNodes.isEmpty()) {
             LOG.info("Topology Provider {} updating {} TCP-MD5 keys", topologyId(), outdatedNodes.size());
@@ -187,8 +186,7 @@ final class PCEPTopologyProvider extends DefaultTopologyReference {
             return;
         }
 
-        proposal = new PCEPStatefulPeerProposal(dependencies.getDataBroker(), instanceIdentifier,
-            currentConfig.getSpeakerIds());
+        proposal = new PCEPStatefulPeerProposal(dependencies.getDataBroker(), instanceIdentifier);
 
         LOG.info("PCEP Topology Provider {} starting server channel", topologyId());
         final var channelFuture = dependencies.getPCEPDispatcher().createServer(
@@ -240,6 +238,7 @@ final class PCEPTopologyProvider extends DefaultTopologyReference {
 
     @Holding("this")
     private void disableManager(final SettableFuture<Empty> future) {
+        proposal.close();
         proposal = null;
         final var managerStop = manager.stop();
         manager = null;
diff --git a/pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/SpeakerIdMapping.java b/pcep/topology/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/SpeakerIdMapping.java
deleted file mode 100644 (file)
index 4995053..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.bgpcep.pcep.topology.provider;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import java.net.InetAddress;
-import java.util.Map;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.concepts.Immutable;
-
-final class SpeakerIdMapping implements Immutable {
-    private static final @NonNull SpeakerIdMapping EMPTY = new SpeakerIdMapping(ImmutableMap.of());
-
-    private final ImmutableMap<InetAddress, byte[]> map;
-
-    private SpeakerIdMapping(final Map<InetAddress, byte[]> map) {
-        this.map = ImmutableMap.copyOf(map);
-    }
-
-    static @NonNull SpeakerIdMapping of() {
-        return EMPTY;
-    }
-
-    static @NonNull SpeakerIdMapping copyOf(final Map<InetAddress, byte[]> map) {
-        return map.isEmpty() ? of()
-            // Defensive: disconnect byte[]s from caller
-            : new SpeakerIdMapping(Maps.transformValues(map, byte[]::clone));
-    }
-
-    byte @Nullable [] speakerIdForAddress(final InetAddress address) {
-        final byte[] found = map.get(address);
-        // Defensive: do not leak byte[]
-        return found == null ? null : found.clone();
-    }
-}
\ No newline at end of file
index 1b697501fec8b500305a2a7a1037d91279e282aa..2d89392f56449f9febc0f3a9114b1e2d6f9b0abd 100644 (file)
@@ -7,42 +7,52 @@
  */
 package org.opendaylight.bgpcep.pcep.topology.provider;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 
 import com.google.common.util.concurrent.FluentFuture;
 import java.net.InetSocketAddress;
-import java.util.Map;
+import java.util.List;
 import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.Stateful1Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.Tlvs3;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.lsp.db.version.tlv.LspDbVersion;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.lsp.db.version.tlv.LspDbVersionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.speaker.entity.id.tlv.SpeakerEntityIdBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Tlvs1Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.stateful.capability.tlv.StatefulBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.open.object.open.TlvsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.sync.optimizations.config.rev181109.PcepNodeSyncConfigBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.Uint64;
 
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class PCEPStatefulPeerProposalTest {
     private static final InstanceIdentifier<Topology> TOPOLOGY_IID = InstanceIdentifier.create(NetworkTopology.class)
             .child(Topology.class, new TopologyKey(new TopologyId("topology")));
@@ -55,65 +65,84 @@ public class PCEPStatefulPeerProposalTest {
     @Mock
     private DataBroker dataBroker;
     @Mock
+    private ListenerRegistration<?> listenerReg;
+    @Mock
     private FluentFuture<Optional<LspDbVersion>> listenableFutureMock;
     @Mock
     private ReadTransaction rt;
+
+    private ArgumentCaptor<DataTreeChangeListener<?>> captor;
     private TlvsBuilder tlvsBuilder;
 
-    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
         tlvsBuilder = new TlvsBuilder().addAugmentation(new Tlvs1Builder()
             .setStateful(new StatefulBuilder().addAugmentation(new Stateful1Builder().build()).build())
             .build());
+        captor = ArgumentCaptor.forClass(DataTreeChangeListener.class);
+        doReturn(listenerReg).when(dataBroker).registerDataTreeChangeListener(any(), captor.capture());
+        doNothing().when(listenerReg).close();
         doReturn(rt).when(dataBroker).newReadOnlyTransaction();
         doNothing().when(rt).close();
-        doReturn(listenableFutureMock).when(rt)
-                .read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class));
-        doReturn(true).when(listenableFutureMock).isDone();
-        doAnswer(invocation -> {
-            final Runnable runnable = (Runnable) invocation.getArguments()[0];
-            runnable.run();
-            return null;
-        }).when(listenableFutureMock).addListener(any(Runnable.class), any(Executor.class));
+        doReturn(listenableFutureMock).when(rt).read(any(), any());
     }
 
     @Test
-    public void testSetPeerProposalSuccess() throws InterruptedException, ExecutionException {
+    public void testSetPeerProposalSuccess() throws Exception {
         doReturn(Optional.of(LSP_DB_VERSION)).when(listenableFutureMock).get();
-        final PCEPStatefulPeerProposal peerProposal = new PCEPStatefulPeerProposal(dataBroker, TOPOLOGY_IID,
-            SpeakerIdMapping.of());
-        peerProposal.setPeerSpecificProposal(ADDRESS, tlvsBuilder);
+        updateBuilder();
         assertEquals(LSP_DB_VERSION, tlvsBuilder.augmentation(Tlvs3.class).getLspDbVersion());
     }
 
     @Test
-    public void testSetPeerProposalWithEntityIdSuccess() throws InterruptedException, ExecutionException {
+    public void testSetPeerProposalWithEntityIdSuccess() throws Exception {
         doReturn(Optional.of(LSP_DB_VERSION)).when(listenableFutureMock).get();
-        final PCEPStatefulPeerProposal peerProposal = new PCEPStatefulPeerProposal(dataBroker, TOPOLOGY_IID,
-            SpeakerIdMapping.copyOf(Map.of(ADDRESS.getAddress(), SPEAKER_ID)));
-        peerProposal.setPeerSpecificProposal(ADDRESS, tlvsBuilder);
+
+        updateBuilder(() -> {
+            final var modification = mock(DataTreeModification.class);
+            doReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
+                // not entirely accurate, but works well enough
+                TOPOLOGY_IID.child(Node.class, new NodeKey(ServerSessionManager.createNodeId(ADDRESS.getAddress())))))
+                .when(modification).getRootPath();
+
+            final var modificationRoot = mock(DataObjectModification.class);
+            doReturn(new PcepNodeSyncConfigBuilder().setSpeakerEntityIdValue(SPEAKER_ID).build()).when(modificationRoot)
+                .getDataAfter();
+            doReturn(modificationRoot).when(modification).getRootNode();
+
+            captor.getValue().onDataTreeChanged(List.of(modification));
+        });
         final Tlvs3 aug = tlvsBuilder.augmentation(Tlvs3.class);
+        assertNotNull(aug);
         assertEquals(LSP_DB_VERSION, aug.getLspDbVersion());
-        assertArrayEquals(SPEAKER_ID, aug.getSpeakerEntityId().getSpeakerEntityIdValue());
+        assertEquals(new SpeakerEntityIdBuilder().setSpeakerEntityIdValue(SPEAKER_ID).build(),
+            aug.getSpeakerEntityId());
     }
 
     @Test
-    public void testSetPeerProposalAbsent() throws InterruptedException, ExecutionException {
+    public void testSetPeerProposalAbsent() throws Exception {
         doReturn(Optional.empty()).when(listenableFutureMock).get();
-        final PCEPStatefulPeerProposal peerProposal = new PCEPStatefulPeerProposal(dataBroker, TOPOLOGY_IID,
-            SpeakerIdMapping.of());
-        peerProposal.setPeerSpecificProposal(ADDRESS, tlvsBuilder);
+        updateBuilder();
         assertNull(tlvsBuilder.augmentation(Tlvs3.class));
     }
 
     @Test
-    public void testSetPeerProposalFailure() throws InterruptedException, ExecutionException {
+    public void testSetPeerProposalFailure() throws Exception {
         doThrow(new InterruptedException()).when(listenableFutureMock).get();
-        final PCEPStatefulPeerProposal peerProposal = new PCEPStatefulPeerProposal(dataBroker, TOPOLOGY_IID,
-            SpeakerIdMapping.of());
-        peerProposal.setPeerSpecificProposal(ADDRESS, tlvsBuilder);
+        updateBuilder();
         assertNull(tlvsBuilder.augmentation(Tlvs3.class));
     }
+
+    private void updateBuilder() {
+        updateBuilder(null);
+    }
+
+    private void updateBuilder(final Runnable customizer) {
+        try (var proposal = new PCEPStatefulPeerProposal(dataBroker, TOPOLOGY_IID)) {
+            if (customizer != null) {
+                customizer.run();
+            }
+            proposal.setPeerSpecificProposal(ADDRESS, tlvsBuilder);
+        }
+    }
 }