From: Tony Tkacik Date: Wed, 7 Jan 2015 10:35:40 +0000 (+0000) Subject: Merge "Model dom-broker statistics" X-Git-Tag: release/lithium~722 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=d80bf0f81bdeed907b290b67f26f1a3541ad3ea4;hp=a9406b1074eecabc79d13cd47f1c0f221b99e428 Merge "Model dom-broker statistics" --- diff --git a/features/config/pom.xml b/features/config/pom.xml index 461427c7ce..1fa248615c 100644 --- a/features/config/pom.xml +++ b/features/config/pom.xml @@ -34,18 +34,6 @@ org.opendaylight.controller sal-common - - org.opendaylight.controller - sal-common-api - - - org.opendaylight.controller - sal-common-impl - - - org.opendaylight.controller - sal-common-util - org.opendaylight.controller config-api diff --git a/features/config/src/main/resources/features.xml b/features/config/src/main/resources/features.xml index b4dd03f491..b2e0b246ef 100644 --- a/features/config/src/main/resources/features.xml +++ b/features/config/src/main/resources/features.xml @@ -6,21 +6,12 @@ mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features - odl-mdsal-common odl-config-api odl-config-netty-config-api odl-config-core odl-config-manager - - odl-yangtools-data-binding - mvn:org.opendaylight.controller/sal-common/${mdsal.version} - mvn:org.opendaylight.controller/sal-common-api/${mdsal.version} - mvn:org.opendaylight.controller/sal-common-impl/${mdsal.version} - mvn:org.opendaylight.controller/sal-common-util/${mdsal.version} - - mvn:org.opendaylight.controller/config-api/${project.version} odl-yangtools-common @@ -39,7 +30,6 @@ odl-yangtools-common odl-yangtools-binding odl-yangtools-binding-generator - odl-mdsal-common odl-config-api mvn:org.opendaylight.controller/config-util/${project.version} mvn:org.opendaylight.controller/yang-jmx-generator/${project.version} @@ -53,4 +43,4 @@ odl-config-core mvn:org.opendaylight.controller/config-manager/${project.version} - \ No newline at end of file + diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index d81da186b9..5e6afd248f 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -139,6 +139,18 @@ sal-akka-raft ${mdsal.version} + + org.opendaylight.controller + sal-common-api + + + org.opendaylight.controller + sal-common-impl + + + org.opendaylight.controller + sal-common-util + org.opendaylight.controller sal-core-spi diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 540cea1bbc..1582f45789 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -14,6 +14,13 @@ odl-mdsal-xsql odl-toaster + + odl-yangtools-data-binding + mvn:org.opendaylight.controller/sal-common/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-api/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-impl/${mdsal.version} + mvn:org.opendaylight.controller/sal-common-util/${mdsal.version} + odl-yangtools-common odl-yangtools-binding diff --git a/karaf/karaf-parent/pom.xml b/karaf/karaf-parent/pom.xml index c8784a880d..06d8c8d99b 100644 --- a/karaf/karaf-parent/pom.xml +++ b/karaf/karaf-parent/pom.xml @@ -178,6 +178,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html standard + ${karaf.localFeature} diff --git a/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/Activator.java b/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/Activator.java index 80d7083ec0..8c422a52ea 100644 --- a/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/Activator.java +++ b/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/Activator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * @@ -22,6 +21,7 @@ import org.opendaylight.controller.configuration.IConfigurationContainerService; import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase; import org.opendaylight.controller.sal.topology.IListenTopoUpdates; import org.opendaylight.controller.sal.topology.ITopologyService; +import org.opendaylight.controller.switchmanager.IInventoryListener; import org.opendaylight.controller.switchmanager.ISwitchManager; import org.opendaylight.controller.topologymanager.ITopologyManager; import org.opendaylight.controller.topologymanager.ITopologyManagerAware; @@ -73,6 +73,7 @@ public class Activator extends ComponentActivatorAbstractBase { props.put("cachenames", propSet); c.setInterface(new String[] { IListenTopoUpdates.class.getName(), + IInventoryListener.class.getName(), ITopologyManager.class.getName(), ITopologyManagerShell.class.getName(), IConfigurationContainerAware.class.getName(), diff --git a/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java b/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java index 659ee7dd81..e1a0ca1e76 100644 --- a/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java +++ b/opendaylight/adsal/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java @@ -34,6 +34,7 @@ import org.opendaylight.controller.sal.utils.IObjectReader; import org.opendaylight.controller.sal.utils.NodeConnectorCreator; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; +import org.opendaylight.controller.switchmanager.IInventoryListener; import org.opendaylight.controller.switchmanager.ISwitchManager; import org.opendaylight.controller.topologymanager.ITopologyManager; import org.opendaylight.controller.topologymanager.ITopologyManagerAware; @@ -58,6 +59,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -76,6 +79,7 @@ public class TopologyManagerImpl implements IConfigurationContainerAware, IListenTopoUpdates, IObjectReader, + IInventoryListener, CommandProvider { protected static final String TOPOEDGESDB = "topologymanager.edgesDB"; protected static final String TOPOHOSTSDB = "topologymanager.hostsDB"; @@ -83,6 +87,8 @@ public class TopologyManagerImpl implements protected static final String TOPOUSERLINKSDB = "topologymanager.userLinksDB"; private static final String USER_LINKS_FILE_NAME = "userTopology.conf"; private static final Logger log = LoggerFactory.getLogger(TopologyManagerImpl.class); + private static final long PENDING_UPDATE_TIMEOUT = 5000L; + private ITopologyService topoService; private IClusterContainerServices clusterContainerService; private IConfigurationContainerService configurationService; @@ -104,7 +110,79 @@ public class TopologyManagerImpl implements private BlockingQueue notifyQ = new LinkedBlockingQueue(); private volatile Boolean shuttingDown = false; private Thread notifyThread; + private final Map> pendingUpdates = + new HashMap>(); + private final BlockingQueue updateQ = + new LinkedBlockingQueue(); + private Timer pendingTimer; + private Thread updateThread; + + private class PendingEdgeUpdate extends TopoEdgeUpdate { + private PendingEdgeUpdate(Edge e, Set p, UpdateType t) { + super(e, p, t); + } + } + + private class UpdateTopology implements Runnable { + @Override + public void run() { + log.trace("Start topology update thread"); + + while (!shuttingDown) { + try { + List list = new ArrayList(); + TopoEdgeUpdate teu = updateQ.take(); + for (; teu != null; teu = updateQ.poll()) { + list.add(teu); + } + + if (!list.isEmpty()) { + log.trace("Update edges: {}", list); + doEdgeUpdate(list); + } + } catch (InterruptedException e) { + if (shuttingDown) { + break; + } + log.warn("Topology update thread interrupted", e); + } catch (Exception e) { + log.error("Exception on topology update thread", e); + } + } + + log.trace("Exit topology update thread"); + } + } + + private class PendingUpdateTask extends TimerTask { + private final Edge edge; + private final Set props; + private final UpdateType type; + + private PendingUpdateTask(Edge e, Set p, UpdateType t) { + edge = e; + props = p; + type = t; + } + + private NodeConnector getHeadNodeConnector() { + return edge.getHeadNodeConnector(); + } + + private void flush() { + log.info("Flush pending topology update: edge {}, type {}", + edge, type); + updateQ.add(new PendingEdgeUpdate(edge, props, type)); + } + @Override + public void run() { + if (removePendingEvent(this)) { + log.warn("Pending topology update timed out: edge{}, type {}", + edge, type); + } + } + } void nonClusterObjectCreate() { edgesDB = new ConcurrentHashMap>(); @@ -210,6 +288,8 @@ public class TopologyManagerImpl implements // Restore the shuttingDown status on init of the component shuttingDown = false; notifyThread = new Thread(new TopologyNotify(notifyQ)); + pendingTimer = new Timer("Topology Pending Update Timer"); + updateThread = new Thread(new UpdateTopology(), "Topology Update"); } @SuppressWarnings({ "unchecked" }) @@ -277,6 +357,8 @@ public class TopologyManagerImpl implements * */ void started() { + updateThread.start(); + // Start the batcher thread for the cluster wide topology updates notifyThread.start(); // SollicitRefresh MUST be called here else if called at init @@ -287,7 +369,9 @@ public class TopologyManagerImpl implements void stop() { shuttingDown = true; + updateThread.interrupt(); notifyThread.interrupt(); + pendingTimer.cancel(); } /** @@ -297,6 +381,9 @@ public class TopologyManagerImpl implements * */ void destroy() { + updateQ.clear(); + updateThread = null; + pendingTimer = null; notifyQ.clear(); notifyThread = null; } @@ -571,17 +658,100 @@ public class TopologyManagerImpl implements return (switchManager.doesNodeConnectorExist(head)); } + private void addPendingEvent(Edge e, Set p, UpdateType t) { + NodeConnector head = e.getHeadNodeConnector(); + PendingUpdateTask task = new PendingUpdateTask(e, p, t); + synchronized (pendingUpdates) { + List list = pendingUpdates.get(head); + if (list == null) { + list = new LinkedList(); + pendingUpdates.put(head, list); + } + list.add(task); + pendingTimer.schedule(task, PENDING_UPDATE_TIMEOUT); + } + } + + private boolean enqueueEventIfPending(Edge e, Set p, UpdateType t) { + NodeConnector head = e.getHeadNodeConnector(); + synchronized (pendingUpdates) { + List list = pendingUpdates.get(head); + if (list != null) { + log.warn("Enqueue edge update: edge {}, type {}", e, t); + PendingUpdateTask task = new PendingUpdateTask(e, p, t); + list.add(task); + pendingTimer.schedule(task, PENDING_UPDATE_TIMEOUT); + return true; + } + } + + return false; + } + + private boolean removePendingEvent(PendingUpdateTask t) { + t.cancel(); + NodeConnector head = t.getHeadNodeConnector(); + boolean removed = false; + + synchronized (pendingUpdates) { + List list = pendingUpdates.get(head); + if (list != null) { + removed = list.remove(t); + if (list.isEmpty()) { + pendingUpdates.remove(head); + } + } + } + + return removed; + } + + private void removePendingEvent(NodeConnector head, boolean doFlush) { + List list; + synchronized (pendingUpdates) { + list = pendingUpdates.remove(head); + } + + if (list != null) { + for (PendingUpdateTask task : list) { + if (task.cancel() && doFlush) { + task.flush(); + } + } + pendingTimer.purge(); + } + } + private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type, Set props) { - switch (type) { - case ADDED: + return edgeUpdate(e, type, props, false); + } + private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type, Set props, boolean isPending) { + if (!type.equals(UpdateType.ADDED) && + enqueueEventIfPending(e, props, type)) { + return null; + } + switch (type) { + case ADDED: if (this.edgesDB.containsKey(e)) { // Avoid redundant updates (e.g. cluster switch-over) as notifications trigger expensive tasks log.trace("Skipping redundant edge addition: {}", e); return null; } + // Ensure that head node connector exists + if (!isPending) { + if (headNodeConnectorExist(e)) { + removePendingEvent(e.getHeadNodeConnector(), true); + } else { + log.warn("Ignore edge that contains invalid node connector: {}", + e); + addPendingEvent(e, props, type); + return null; + } + } + // Make sure the props are non-null or create a copy if (props == null) { props = new HashSet(); @@ -589,13 +759,6 @@ public class TopologyManagerImpl implements props = new HashSet(props); } - - // Ensure that head node connector exists - if (!headNodeConnectorExist(e)) { - log.warn("Ignore edge that contains invalid node connector: {}", e); - return null; - } - // Check if nodeConnectors of the edge were correctly categorized // by protocol plugin crossCheckNodeConnectors(e); @@ -702,16 +865,16 @@ public class TopologyManagerImpl implements return new TopoEdgeUpdate(e, props, type); } - @Override - public void edgeUpdate(List topoedgeupdateList) { + private void doEdgeUpdate(List topoedgeupdateList) { List teuList = new ArrayList(); - for (int i = 0; i < topoedgeupdateList.size(); i++) { - Edge e = topoedgeupdateList.get(i).getEdge(); - Set p = topoedgeupdateList.get(i).getProperty(); - UpdateType type = topoedgeupdateList.get(i).getUpdateType(); - TopoEdgeUpdate teu = edgeUpdate(e, type, p); - if (teu != null) { - teuList.add(teu); + for (TopoEdgeUpdate teu : topoedgeupdateList) { + boolean isPending = (teu instanceof PendingEdgeUpdate); + Edge e = teu.getEdge(); + Set p = teu.getProperty(); + UpdateType type = teu.getUpdateType(); + TopoEdgeUpdate update = edgeUpdate(e, type, p, isPending); + if (update != null) { + teuList.add(update); } } @@ -727,6 +890,11 @@ public class TopologyManagerImpl implements } } + @Override + public void edgeUpdate(List topoedgeupdateList) { + updateQ.addAll(topoedgeupdateList); + } + private Edge getReverseLinkTuple(TopologyUserLinkConfig link) { TopologyUserLinkConfig rLink = new TopologyUserLinkConfig( link.getName(), link.getDstNodeConnector(), link.getSrcNodeConnector()); @@ -934,6 +1102,19 @@ public class TopologyManagerImpl implements notifyQ.add(upd); } + @Override + public void notifyNode(Node node, UpdateType type, Map propMap) { + // NOP + } + + @Override + public void notifyNodeConnector(NodeConnector nc, UpdateType type, Map propMap) { + // Remove pending edge updates for the given node connector. + // Pending events should be notified if the node connector exists. + boolean doFlush = !type.equals(UpdateType.REMOVED); + removePendingEvent(nc, doFlush); + } + @Override public void entryCreated(final Object key, final String cacheName, final boolean originLocal) { if (cacheName.equals(TOPOEDGESDB)) { @@ -1094,4 +1275,35 @@ public class TopologyManagerImpl implements return result; } + // Only for unit test. + void startTest() { + pendingTimer = new Timer("Topology Pending Update Timer"); + updateThread = new Thread(new UpdateTopology(), "Topology Update"); + updateThread.start(); + } + + void stopTest() { + shuttingDown = true; + updateThread.interrupt(); + pendingTimer.cancel(); + } + + boolean flushUpdateQueue(long timeout) { + long limit = System.currentTimeMillis() + timeout; + long cur; + do { + if (updateQ.peek() == null) { + return true; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + break; + } + cur = System.currentTimeMillis(); + } while (cur < limit); + + return false; + } } diff --git a/opendaylight/adsal/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java b/opendaylight/adsal/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java index d1338bf695..600f1d8cbf 100644 --- a/opendaylight/adsal/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java +++ b/opendaylight/adsal/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java @@ -9,6 +9,8 @@ package org.opendaylight.controller.topologymanager.internal; import org.junit.Assert; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.ConstructionException; @@ -50,6 +52,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentMap; public class TopologyManagerImplTest { + private TopologyManagerImpl topoManagerImpl; + /** * Mockup of switch manager that only maintains existence of node * connector. @@ -78,6 +82,11 @@ public class TopologyManagerImplTest { } } + private void clear() { + nodeSet.clear(); + nodeConnectorSet.clear(); + } + @Override public Status addSubnet(SubnetConfig configObject) { return null; @@ -325,6 +334,20 @@ public class TopologyManagerImplTest { } } + @Before + public void setUp() { + topoManagerImpl = new TopologyManagerImpl(); + topoManagerImpl.startTest(); + } + + @After + public void tearDown() { + if (topoManagerImpl != null) { + topoManagerImpl.stopTest(); + topoManagerImpl = null; + } + } + /* * Sets the node, edges and properties for edges here: Edge : <1:1>--><11:11>; <1:2>--><11:12>; <3:3>--><13:13>; @@ -375,11 +398,12 @@ public class TopologyManagerImplTest { topoedgeupdateList.add(teu2); topoManagerImpl.edgeUpdate(topoedgeupdateList); } + + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); } @Test public void testGetNodeEdges() throws ConstructionException { - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); setNodeEdges(topoManagerImpl, swMgr); @@ -412,7 +436,6 @@ public class TopologyManagerImplTest { @Test public void testGetEdges() throws ConstructionException { - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); setNodeEdges(topoManagerImpl, swMgr); @@ -496,7 +519,6 @@ public class TopologyManagerImplTest { TopologyUserLinkConfig link4 = new TopologyUserLinkConfig("default20", "OF|10@OF|20", "OF|10@OF|30"); - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); topoManagerImpl.nonClusterObjectCreate(); @@ -529,7 +551,6 @@ public class TopologyManagerImplTest { public void testGetUserLink() { TopologyUserLinkConfig[] link = new TopologyUserLinkConfig[5]; TopologyUserLinkConfig[] reverseLink = new TopologyUserLinkConfig[5]; - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); topoManagerImpl.nonClusterObjectCreate(); @@ -614,7 +635,6 @@ public class TopologyManagerImplTest { @Test public void testHostLinkMethods() throws ConstructionException, UnknownHostException { - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); topoManagerImpl.nonClusterObjectCreate(); @@ -678,7 +698,6 @@ public class TopologyManagerImplTest { @Test public void testGetNodesWithNodeConnectorHost() throws ConstructionException, UnknownHostException { - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); topoManagerImpl.nonClusterObjectCreate(); @@ -738,7 +757,6 @@ public class TopologyManagerImplTest { @Test public void bug1348FixTest() throws ConstructionException { - TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); TestSwitchManager swMgr = new TestSwitchManager(); topoManagerImpl.setSwitchManager(swMgr); topoManagerImpl.nonClusterObjectCreate(); @@ -763,7 +781,91 @@ public class TopologyManagerImplTest { Assert.fail("Exception was raised when trying to update edge properties: " + e.getMessage()); } + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); Assert.assertEquals(1, topoManagerImpl.getEdges().size()); Assert.assertNotNull(topoManagerImpl.getEdges().get(edge)); } + + @Test + public void testNotifyNodeConnector() throws ConstructionException { + TestSwitchManager swMgr = new TestSwitchManager(); + topoManagerImpl.setSwitchManager(swMgr); + topoManagerImpl.nonClusterObjectCreate(); + + // Test NodeConnector notification in the case that there are no + // related edge updates. + NodeConnector nc1 = NodeConnectorCreator.createOFNodeConnector( + (short) 1, NodeCreator.createOFNode(1000L)); + Map propMap = new HashMap<>(); + swMgr.addNodeConnectors(nc1); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.ADDED, propMap); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.CHANGED, propMap); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + + swMgr.clear(); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + + // Test NodeConnector notification in the case that there is a related + // edge update just before the notification. + NodeConnector nc2 = NodeConnectorCreator.createOFNodeConnector( + (short) 2, NodeCreator.createOFNode(2000L)); + Edge edge1 = new Edge(nc1, nc2); + Edge edge2 = new Edge(nc2, nc1); + Set props = new HashSet(); + TopoEdgeUpdate teu1 = new TopoEdgeUpdate(edge1, props, UpdateType.ADDED); + TopoEdgeUpdate teu2 = new TopoEdgeUpdate(edge2, props, UpdateType.ADDED); + List topoedgeupdateList = new ArrayList(); + topoedgeupdateList.add(teu1); + topoedgeupdateList.add(teu2); + topoManagerImpl.edgeUpdate(topoedgeupdateList); + swMgr.addNodeConnectors(nc1); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.ADDED, propMap); + swMgr.addNodeConnectors(nc2); + topoManagerImpl.notifyNodeConnector(nc2, UpdateType.CHANGED, propMap); + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); + Assert.assertEquals(2, topoManagerImpl.getEdges().size()); + + teu1 = new TopoEdgeUpdate(edge1, props, UpdateType.REMOVED); + teu2 = new TopoEdgeUpdate(edge2, props, UpdateType.REMOVED); + topoedgeupdateList = new ArrayList(); + topoedgeupdateList.add(teu1); + topoedgeupdateList.add(teu2); + topoManagerImpl.edgeUpdate(topoedgeupdateList); + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap); + topoManagerImpl.notifyNodeConnector(nc2, UpdateType.REMOVED, propMap); + + swMgr.clear(); + + // Test NodeConnector notification in the case that there are multiple + // edge updates related to the NodeConnector just before the notification. + teu1 = new TopoEdgeUpdate(edge1, props, UpdateType.ADDED); + teu2 = new TopoEdgeUpdate(edge2, props, UpdateType.ADDED); + TopoEdgeUpdate teu3 = new TopoEdgeUpdate(edge1, props, UpdateType.CHANGED); + TopoEdgeUpdate teu4 = new TopoEdgeUpdate(edge2, props, UpdateType.CHANGED); + TopoEdgeUpdate teu5 = new TopoEdgeUpdate(edge1, props, UpdateType.REMOVED); + TopoEdgeUpdate teu6 = new TopoEdgeUpdate(edge2, props, UpdateType.REMOVED); + topoedgeupdateList = new ArrayList(); + topoedgeupdateList.add(teu1); + topoedgeupdateList.add(teu2); + topoedgeupdateList.add(teu3); + topoedgeupdateList.add(teu4); + topoedgeupdateList.add(teu5); + topoedgeupdateList.add(teu6); + topoManagerImpl.edgeUpdate(topoedgeupdateList); + swMgr.addNodeConnectors(nc1); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.ADDED, propMap); + swMgr.addNodeConnectors(nc2); + topoManagerImpl.notifyNodeConnector(nc2, UpdateType.CHANGED, propMap); + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap); + topoManagerImpl.notifyNodeConnector(nc2, UpdateType.REMOVED, propMap); + Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000)); + Assert.assertEquals(0, topoManagerImpl.getEdges().size()); + } } diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java index de367eaf9c..0346bdd19c 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java @@ -74,7 +74,7 @@ abstract class AbstractDynamicWrapper implements DynamicMBeanModuleWrapper { public void handleNotification(final Notification n, final Object handback) { if (n instanceof MBeanServerNotification && n.getType() - .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) { + .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) { if (((MBeanServerNotification) n).getMBeanName().equals( thisWrapperObjectName)) { try { diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java index c20d3bfc04..a15839d2d5 100644 --- a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadPoolExecutor; -import javax.management.DynamicMBean; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; diff --git a/opendaylight/config/config-parent/pom.xml b/opendaylight/config/config-parent/pom.xml new file mode 100644 index 0000000000..0b2b634170 --- /dev/null +++ b/opendaylight/config/config-parent/pom.xml @@ -0,0 +1,154 @@ + + + + + org.opendaylight.yangtools + binding-parent + 0.7.0-SNAPSHOT + + + + 4.0.0 + org.opendaylight.controller + config-parent + 0.3.0-SNAPSHOT + pom + + + 0.3.0-SNAPSHOT + 1.2.0-SNAPSHOT + src/main/yang-gen-config + src/main/config/default-config.xml + + + + + + + org.opendaylight.controller + config-artifacts + ${config.version} + pom + import + + + org.opendaylight.controller + mdsal-artifacts + ${mdsal.version} + pom + import + + + + + + + org.opendaylight.controller + config-api + + + org.opendaylight.controller + sal-binding-config + + + + + + + org.opendaylight.yangtools + yang-maven-plugin + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.version} + + + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${jmxGeneratorPath} + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + true + + + + + + maven-clean-plugin + + + + ${jmxGeneratorPath} + + ** + + + + + + + + + + + + + ${config.file} + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${config.file} + xml + config + + + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + + diff --git a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigSnapshot.java b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigSnapshot.java index 4f050640c5..589a644f3d 100644 --- a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigSnapshot.java +++ b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigSnapshot.java @@ -36,6 +36,7 @@ public class ConfigSnapshot { return new ConfigSnapshot(cfg.getConfigSnapshot(), cfg.getCapabilities()); } + @XmlAnyElement(SnapshotHandler.class) public String getConfigSnapshot() { return configSnapshot; diff --git a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/SnapshotHandler.java b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/SnapshotHandler.java index 8214b36970..dacc35b83e 100644 --- a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/SnapshotHandler.java +++ b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/SnapshotHandler.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.config.persist.storage.file.xml.model; +import com.google.common.base.Preconditions; import java.io.StringReader; import java.io.StringWriter; import javax.xml.bind.ValidationEventHandler; @@ -31,6 +32,8 @@ class SnapshotHandler implements DomHandler { String xml = rt.getWriter().toString(); int beginIndex = xml.indexOf(START_TAG) + START_TAG.length(); int endIndex = xml.indexOf(END_TAG); + Preconditions.checkArgument(beginIndex != -1 && endIndex != -1, + "Unknown element present in config snapshot(expected only configuration): %s", xml); return xml.substring(beginIndex, endIndex); } diff --git a/opendaylight/config/config-persister-file-xml-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigTest.java b/opendaylight/config/config-persister-file-xml-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigTest.java new file mode 100644 index 0000000000..23b7abee39 --- /dev/null +++ b/opendaylight/config/config-persister-file-xml-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/xml/model/ConfigTest.java @@ -0,0 +1,12 @@ +package org.opendaylight.controller.config.persist.storage.file.xml.model; + +import java.io.File; +import org.junit.Test; + +public class ConfigTest { + + @Test(expected = IllegalArgumentException.class) + public void testFromXml() throws Exception { + Config.fromXml(new File(getClass().getResource("/illegalSnapshot.xml").getFile())); + } +} \ No newline at end of file diff --git a/opendaylight/config/config-persister-file-xml-adapter/src/test/resources/illegalSnapshot.xml b/opendaylight/config/config-persister-file-xml-adapter/src/test/resources/illegalSnapshot.xml new file mode 100644 index 0000000000..f3fd13a36d --- /dev/null +++ b/opendaylight/config/config-persister-file-xml-adapter/src/test/resources/illegalSnapshot.xml @@ -0,0 +1,46 @@ + + + + + + + + + + prefix:clustering-service-provider + + clustering-service-provider + + + binding:binding-rpc-registry + binding-rpc-broker + + + + + + + + + + + prefix:clustering-service-change-registry + + openflow-role-change-registry + /modules/module[type='clustering-service-provider'][name='clustering-service-provider'] + + + + + + urn:opendaylight:params:xml:ns:yang:controller:config:clustering-service-provider?module=clustering-service-provider&revision=2014-11-19 + + + + \ No newline at end of file diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index fc447aa7f9..4c4c5b3378 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -43,6 +43,7 @@ config-netty-config config-artifacts + config-parent @@ -145,7 +146,7 @@ maven-checkstyle-plugin false - false + true checkstyle-logging.xml true true @@ -154,7 +155,7 @@ - **\/logback-config\/,**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/ + **\/config\/yang\/logback\/config\/**,**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/ diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ClientActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ClientActor.java index 59bec91511..8022e72157 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ClientActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ClientActor.java @@ -13,7 +13,6 @@ import akka.actor.Props; import akka.actor.UntypedActor; import akka.event.Logging; import akka.event.LoggingAdapter; -import akka.japi.Creator; import org.opendaylight.controller.cluster.example.messages.KeyValue; import org.opendaylight.controller.cluster.example.messages.KeyValueSaved; @@ -27,14 +26,8 @@ public class ClientActor extends UntypedActor { this.target = target; } - public static Props props(final ActorRef target){ - return Props.create(new Creator(){ - private static final long serialVersionUID = 1L; - - @Override public ClientActor create() throws Exception { - return new ClientActor(target); - } - }); + public static Props props(final ActorRef target) { + return Props.create(ClientActor.class, target); } @Override public void onReceive(Object message) throws Exception { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java index 6c65021d86..684c3ac30e 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java @@ -10,7 +10,6 @@ package org.opendaylight.controller.cluster.example; import akka.actor.ActorRef; import akka.actor.Props; -import akka.japi.Creator; import com.google.common.base.Optional; import com.google.protobuf.ByteString; import java.io.ByteArrayInputStream; @@ -53,13 +52,8 @@ public class ExampleActor extends RaftActor { } public static Props props(final String id, final Map peerAddresses, - final Optional configParams){ - return Props.create(new Creator(){ - - @Override public ExampleActor create() throws Exception { - return new ExampleActor(id, peerAddresses, configParams); - } - }); + final Optional configParams) { + return Props.create(ExampleActor.class, id, peerAddresses, configParams); } @Override public void onReceiveCommand(Object message) throws Exception{ diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleRoleChangeListener.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleRoleChangeListener.java index c0ee095367..1676a41c56 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleRoleChangeListener.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleRoleChangeListener.java @@ -1,10 +1,8 @@ package org.opendaylight.controller.cluster.example; -import akka.actor.Actor; import akka.actor.ActorRef; import akka.actor.Cancellable; import akka.actor.Props; -import akka.japi.Creator; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -46,12 +44,7 @@ public class ExampleRoleChangeListener extends AbstractUntypedActor implements A } public static Props getProps(final String memberName) { - return Props.create(new Creator() { - @Override - public Actor create() throws Exception { - return new ExampleRoleChangeListener(memberName); - } - }); + return Props.create(ExampleRoleChangeListener.class, memberName); } @Override diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/notifications/RoleChangeNotifier.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/notifications/RoleChangeNotifier.java index a9aa56174d..d065f6d211 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/notifications/RoleChangeNotifier.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/notifications/RoleChangeNotifier.java @@ -8,11 +8,9 @@ package org.opendaylight.controller.cluster.notifications; -import akka.actor.Actor; import akka.actor.ActorPath; import akka.actor.ActorRef; import akka.actor.Props; -import akka.japi.Creator; import akka.serialization.Serialization; import com.google.common.collect.Maps; import java.util.Map; @@ -35,12 +33,7 @@ public class RoleChangeNotifier extends AbstractUntypedActor implements AutoClos } public static Props getProps(final String memberId) { - return Props.create(new Creator() { - @Override - public Actor create() throws Exception { - return new RoleChangeNotifier(memberId); - } - }); + return Props.create(RoleChangeNotifier.class, memberId); } @Override diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/common/actor/MeteredBoundedMailboxTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/common/actor/MeteredBoundedMailboxTest.java index 60efb9d7ca..b706d20d1a 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/common/actor/MeteredBoundedMailboxTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/common/actor/MeteredBoundedMailboxTest.java @@ -12,7 +12,6 @@ import akka.actor.ActorSystem; import akka.actor.DeadLetter; import akka.actor.Props; import akka.actor.UntypedActor; -import akka.japi.Creator; import akka.testkit.JavaTestKit; import org.junit.After; import org.junit.Before; @@ -80,13 +79,7 @@ public class MeteredBoundedMailboxTest { } public static Props props(final ReentrantLock lock){ - return Props.create(new Creator(){ - private static final long serialVersionUID = 1L; - @Override - public PingPongActor create() throws Exception { - return new PingPongActor(lock); - } - }); + return Props.create(PingPongActor.class, lock); } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java index 38254dd01a..7407897dfa 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java @@ -669,7 +669,7 @@ public class TransactionProxyTest { } @Test(expected=IllegalStateException.class) - public void testxistsPreConditionCheck() { + public void testExistsPreConditionCheck() { TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java index fb8822d07c..4cbce63f9a 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java @@ -13,8 +13,8 @@ import akka.actor.ActorRef; import akka.actor.OneForOneStrategy; import akka.actor.Props; import akka.actor.SupervisorStrategy; -import akka.japi.Creator; import akka.japi.Function; +import java.util.Set; import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor; import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; @@ -26,8 +26,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.concurrent.duration.Duration; -import java.util.Set; - /** * This class acts as a supervisor, creates all the actors, resumes them, if an exception is thrown. * @@ -61,17 +59,10 @@ public class RpcManager extends AbstractUntypedActor { } - public static Props props(final SchemaContext schemaContext, - final Broker.ProviderSession brokerSession, - final RpcProvisionRegistry rpcProvisionRegistry) { - return Props.create(new Creator() { - private static final long serialVersionUID = 1L; - @Override - public RpcManager create() throws Exception { - return new RpcManager(schemaContext, brokerSession, rpcProvisionRegistry); - } - }); - } + public static Props props(final SchemaContext schemaContext, final Broker.ProviderSession brokerSession, + final RpcProvisionRegistry rpcProvisionRegistry) { + return Props.create(RpcManager.class, schemaContext, brokerSession, rpcProvisionRegistry); + } private void createRpcActors() { LOG.debug("Create rpc registry and broker actors"); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java index fe8c463d2e..52b1106c87 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RoutingTable.java @@ -10,23 +10,22 @@ package org.opendaylight.controller.remote.rpc.registry; import akka.actor.ActorRef; import akka.japi.Option; import akka.japi.Pair; -import org.opendaylight.controller.remote.rpc.registry.gossip.Copier; -import org.opendaylight.controller.sal.connector.api.RpcRouter; - import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import org.opendaylight.controller.remote.rpc.registry.gossip.Copier; +import org.opendaylight.controller.sal.connector.api.RpcRouter; public class RoutingTable implements Copier, Serializable { private static final long serialVersionUID = 1L; - private Map, Long> table = new HashMap<>(); + private final Map, Long> table = new HashMap<>(); private ActorRef router; @Override public RoutingTable copy() { RoutingTable copy = new RoutingTable(); - copy.setTable(new HashMap<>(table)); + copy.table.putAll(table); copy.setRouter(this.getRouter()); return copy; @@ -35,10 +34,11 @@ public class RoutingTable implements Copier, Serializable { public Option> getRouterFor(RpcRouter.RouteIdentifier routeId){ Long updatedTime = table.get(routeId); - if (updatedTime == null || router == null) + if (updatedTime == null || router == null) { return Option.none(); - else + } else { return Option.option(new Pair<>(router, updatedTime)); + } } public void addRoute(RpcRouter.RouteIdentifier routeId){ @@ -49,23 +49,16 @@ public class RoutingTable implements Copier, Serializable { table.remove(routeId); } - public Boolean contains(RpcRouter.RouteIdentifier routeId){ + public boolean contains(RpcRouter.RouteIdentifier routeId){ return table.containsKey(routeId); } - public Boolean isEmpty(){ + public boolean isEmpty(){ return table.isEmpty(); } - /// - /// Getter, Setters - /// - //TODO: Remove public - public Map, Long> getTable() { - return table; - } - void setTable(Map, Long> table) { - this.table = table; + public int size() { + return table.size(); } public ActorRef getRouter() { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index 095d70926b..845c1c819a 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -8,36 +8,21 @@ package org.opendaylight.controller.remote.rpc.registry; import akka.actor.ActorRef; -import akka.actor.Address; -import akka.actor.Props; -import akka.dispatch.Mapper; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.japi.Option; import akka.japi.Pair; -import akka.pattern.Patterns; import com.google.common.base.Preconditions; -import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActorWithMetering; -import org.opendaylight.controller.remote.rpc.RemoteRpcProviderConfig; +import java.util.ArrayList; +import java.util.List; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; import org.opendaylight.controller.remote.rpc.registry.gossip.Bucket; import org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore; import org.opendaylight.controller.sal.connector.api.RpcRouter; -import scala.concurrent.Future; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBucketsReply; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucket; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucketReply; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateBucket; +import org.opendaylight.controller.sal.connector.api.RpcRouter.RouteIdentifier; /** * Registry to look up cluster nodes that have registered for a given rpc. @@ -45,51 +30,29 @@ import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.Bu * It uses {@link org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore} to maintain this * cluster wide information. */ -public class RpcRegistry extends AbstractUntypedActorWithMetering { +public class RpcRegistry extends BucketStore { final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - /** - * Store to keep the registry. Bucket store sync's it across nodes in the cluster - */ - private ActorRef bucketStore; - - /** - * Rpc broker that would use the registry to route requests. - */ - private ActorRef localRouter; - - private RemoteRpcProviderConfig config; - public RpcRegistry() { - bucketStore = getContext().actorOf(Props.create(BucketStore.class), "store"); - this.config = new RemoteRpcProviderConfig(getContext().system().settings().config()); - log.info("Bucket store path = {}", bucketStore.path().toString()); + getLocalBucket().setData(new RoutingTable()); } - public RpcRegistry(ActorRef bucketStore) { - this.bucketStore = bucketStore; - } - - @Override protected void handleReceive(Object message) throws Exception { //TODO: if sender is remote, reject message - if (message instanceof SetLocalRouter) + if (message instanceof SetLocalRouter) { receiveSetLocalRouter((SetLocalRouter) message); - - if (message instanceof AddOrUpdateRoutes) + } else if (message instanceof AddOrUpdateRoutes) { receiveAddRoutes((AddOrUpdateRoutes) message); - - else if (message instanceof RemoveRoutes) + } else if (message instanceof RemoveRoutes) { receiveRemoveRoutes((RemoveRoutes) message); - - else if (message instanceof Messages.FindRouters) + } else if (message instanceof Messages.FindRouters) { receiveGetRouter((FindRouters) message); - - else - unhandled(message); + } else { + super.handleReceive(message); + } } /** @@ -98,7 +61,7 @@ public class RpcRegistry extends AbstractUntypedActorWithMetering { * @param message contains {@link akka.actor.ActorRef} for rpc broker */ private void receiveSetLocalRouter(SetLocalRouter message) { - localRouter = message.getRouter(); + getLocalBucket().getData().setRouter(message.getRouter()); } /** @@ -106,10 +69,14 @@ public class RpcRegistry extends AbstractUntypedActorWithMetering { */ private void receiveAddRoutes(AddOrUpdateRoutes msg) { - Preconditions.checkState(localRouter != null, "Router must be set first"); + log.debug("AddOrUpdateRoutes: {}", msg.getRouteIdentifiers()); + + RoutingTable table = getLocalBucket().getData().copy(); + for(RpcRouter.RouteIdentifier routeId : msg.getRouteIdentifiers()) { + table.addRoute(routeId); + } - Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), config.getAskDuration()); - futureReply.map(getMapperToAddRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); + updateLocalBucket(table); } /** @@ -117,9 +84,12 @@ public class RpcRegistry extends AbstractUntypedActorWithMetering { */ private void receiveRemoveRoutes(RemoveRoutes msg) { - Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), config.getAskDuration()); - futureReply.map(getMapperToRemoveRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); + RoutingTable table = getLocalBucket().getData().copy(); + for (RpcRouter.RouteIdentifier routeId : msg.getRouteIdentifiers()) { + table.removeRoute(routeId); + } + updateLocalBucket(table); } /** @@ -128,168 +98,28 @@ public class RpcRegistry extends AbstractUntypedActorWithMetering { * @param msg */ private void receiveGetRouter(FindRouters msg) { - final ActorRef sender = getSender(); - - Future futureReply = Patterns.ask(bucketStore, new GetAllBuckets(), config.getAskDuration()); - futureReply.map(getMapperToGetRouter(msg.getRouteIdentifier(), sender), getContext().dispatcher()); - } - - /** - * Helper to create empty reply when no routers are found - * - * @return - */ - private Messages.FindRoutersReply createEmptyReply() { - List> routerWithUpdateTime = Collections.emptyList(); - return new Messages.FindRoutersReply(routerWithUpdateTime); - } - - /** - * Helper to create a reply when routers are found for the given rpc - * - * @param buckets - * @param routeId - * @return - */ - private Messages.FindRoutersReply createReplyWithRouters( - Map buckets, RpcRouter.RouteIdentifier routeId) { - List> routers = new ArrayList<>(); - Option> routerWithUpdateTime = null; - - for (Bucket bucket : buckets.values()) { - - RoutingTable table = (RoutingTable) bucket.getData(); - if (table == null) - continue; - routerWithUpdateTime = table.getRouterFor(routeId); - if (routerWithUpdateTime.isEmpty()) - continue; + RouteIdentifier routeId = msg.getRouteIdentifier(); + findRoutes(getLocalBucket().getData(), routeId, routers); - routers.add(routerWithUpdateTime.get()); + for(Bucket bucket : getRemoteBuckets().values()) { + findRoutes(bucket.getData(), routeId, routers); } - return new Messages.FindRoutersReply(routers); - } - - - /// - ///private factories to create Mapper - /// - - /** - * Receives all buckets returned from bucket store and finds routers for the buckets where given rpc(routeId) is found - * - * @param routeId the rpc - * @param sender client who asked to find the routers. - * @return - */ - private Mapper getMapperToGetRouter( - final RpcRouter.RouteIdentifier routeId, final ActorRef sender) { - return new Mapper() { - @Override - public Void apply(Object replyMessage) { - - if (replyMessage instanceof GetAllBucketsReply) { - - GetAllBucketsReply reply = (GetAllBucketsReply) replyMessage; - Map buckets = reply.getBuckets(); - - if (buckets == null || buckets.isEmpty()) { - sender.tell(createEmptyReply(), getSelf()); - return null; - } - - sender.tell(createReplyWithRouters(buckets, routeId), getSelf()); - } - return null; - } - }; - } - - /** - * Receives local bucket from bucket store and updates routing table in it by removing the route. Subsequently, - * it updates the local bucket in bucket store. - * - * @param routeIds rpc to remote - * @return - */ - private Mapper getMapperToRemoveRoutes(final List> routeIds) { - return new Mapper() { - @Override - public Void apply(Object replyMessage) { - if (replyMessage instanceof GetLocalBucketReply) { - - GetLocalBucketReply reply = (GetLocalBucketReply) replyMessage; - Bucket bucket = reply.getBucket(); - - if (bucket == null) { - log.debug("Local bucket is null"); - return null; - } - - RoutingTable table = bucket.getData(); - if (table == null) - table = new RoutingTable(); - - table.setRouter(localRouter); - - if (!table.isEmpty()) { - for (RpcRouter.RouteIdentifier routeId : routeIds) { - table.removeRoute(routeId); - } - } - bucket.setData(table); - - UpdateBucket updateBucketMessage = new UpdateBucket(bucket); - bucketStore.tell(updateBucketMessage, getSelf()); - } - return null; - } - }; + getSender().tell(new Messages.FindRoutersReply(routers), getSelf()); } - /** - * Receives local bucket from bucket store and updates routing table in it by adding the route. Subsequently, - * it updates the local bucket in bucket store. - * - * @param routeIds rpc to add - * @return - */ - private Mapper getMapperToAddRoutes(final List> routeIds) { - - return new Mapper() { - @Override - public Void apply(Object replyMessage) { - if (replyMessage instanceof GetLocalBucketReply) { - - GetLocalBucketReply reply = (GetLocalBucketReply) replyMessage; - Bucket bucket = reply.getBucket(); - - if (bucket == null) { - log.debug("Local bucket is null"); - return null; - } - - RoutingTable table = bucket.getData(); - if (table == null) - table = new RoutingTable(); - - table.setRouter(localRouter); - for (RpcRouter.RouteIdentifier routeId : routeIds) { - table.addRoute(routeId); - } - - bucket.setData(table); - - UpdateBucket updateBucketMessage = new UpdateBucket(bucket); - bucketStore.tell(updateBucketMessage, getSelf()); - } + private void findRoutes(RoutingTable table, RpcRouter.RouteIdentifier routeId, + List> routers) { + if (table == null) { + return; + } - return null; - } - }; + Option> routerWithUpdateTime = table.getRouterFor(routeId); + if(!routerWithUpdateTime.isEmpty()) { + routers.add(routerWithUpdateTime.get()); + } } /** diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Bucket.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Bucket.java index f5dfbc5650..c40fc9349e 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Bucket.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Bucket.java @@ -11,5 +11,4 @@ package org.opendaylight.controller.remote.rpc.registry.gossip; public interface Bucket> { public Long getVersion(); public T getData(); - public void setData(T data); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketImpl.java index 01c77f1f08..b81175e9a2 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketImpl.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketImpl.java @@ -16,6 +16,23 @@ public class BucketImpl> implements Bucket, Serializable private T data; + public BucketImpl() { + } + + public BucketImpl(T data) { + this.data = data; + } + + public BucketImpl(Bucket other) { + this.version = other.getVersion(); + this.data = other.getData(); + } + + public void setData(T data) { + this.data = data; + this.version = System.currentTimeMillis()+1; + } + @Override public Long getVersion() { return version; @@ -23,15 +40,7 @@ public class BucketImpl> implements Bucket, Serializable @Override public T getData() { - if (this.data == null) - return null; - - return data.copy(); - } - - public void setData(T data){ - this.version = System.currentTimeMillis()+1; - this.data = data; + return data; } @Override diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java index 6ffe147e71..934609b7cf 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java @@ -15,11 +15,10 @@ import akka.actor.Props; import akka.cluster.ClusterActorRefProvider; import akka.event.Logging; import akka.event.LoggingAdapter; +import com.google.common.annotations.VisibleForTesting; import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActorWithMetering; import org.opendaylight.controller.remote.rpc.RemoteRpcProviderConfig; import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets; @@ -28,9 +27,6 @@ import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketSto import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersionsReply; import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembers; import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembersReply; -import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucket; -import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucketReply; -import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateBucket; import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateRemoteBuckets; import org.opendaylight.controller.utils.ConditionalProbe; @@ -43,24 +39,26 @@ import org.opendaylight.controller.utils.ConditionalProbe; * This store uses a {@link org.opendaylight.controller.remote.rpc.registry.gossip.Gossiper}. * */ -public class BucketStore extends AbstractUntypedActorWithMetering { +public class BucketStore> extends AbstractUntypedActorWithMetering { + + private static final Long NO_VERSION = -1L; final LoggingAdapter log = Logging.getLogger(getContext().system(), this); /** * Bucket owned by the node */ - private BucketImpl localBucket = new BucketImpl(); + private final BucketImpl localBucket = new BucketImpl<>(); /** * Buckets ownded by other known nodes in the cluster */ - private ConcurrentMap remoteBuckets = new ConcurrentHashMap<>(); + private final Map> remoteBuckets = new HashMap<>(); /** * Bucket version for every known node in the cluster including this node */ - private ConcurrentMap versions = new ConcurrentHashMap<>(); + private final Map versions = new HashMap<>(); /** * Cluster address for this node @@ -85,7 +83,6 @@ public class BucketStore extends AbstractUntypedActorWithMetering { } } - @Override protected void handleReceive(Object message) throws Exception { if (probe != null) { @@ -98,20 +95,14 @@ public class BucketStore extends AbstractUntypedActorWithMetering { probe = (ConditionalProbe) message; // Send back any message to tell the caller we got the probe. getSender().tell("Got it", getSelf()); - } else if (message instanceof UpdateBucket) { - receiveUpdateBucket(((UpdateBucket) message).getBucket()); } else if (message instanceof GetAllBuckets) { - receiveGetAllBucket(); - } else if (message instanceof GetLocalBucket) { - receiveGetLocalBucket(); + receiveGetAllBuckets(); } else if (message instanceof GetBucketsByMembers) { - receiveGetBucketsByMembers( - ((GetBucketsByMembers) message).getMembers()); + receiveGetBucketsByMembers(((GetBucketsByMembers) message).getMembers()); } else if (message instanceof GetBucketVersions) { receiveGetBucketVersions(); } else if (message instanceof UpdateRemoteBuckets) { - receiveUpdateRemoteBuckets( - ((UpdateRemoteBuckets) message).getBuckets()); + receiveUpdateRemoteBuckets(((UpdateRemoteBuckets) message).getBuckets()); } else { if(log.isDebugEnabled()) { log.debug("Unhandled message [{}]", message); @@ -120,30 +111,10 @@ public class BucketStore extends AbstractUntypedActorWithMetering { } } - /** - * Returns a copy of bucket owned by this node - */ - private void receiveGetLocalBucket() { - final ActorRef sender = getSender(); - GetLocalBucketReply reply = new GetLocalBucketReply(localBucket); - sender.tell(reply, getSelf()); - } - - /** - * Updates the bucket owned by this node - * - * @param updatedBucket - */ - void receiveUpdateBucket(Bucket updatedBucket){ - - localBucket = (BucketImpl) updatedBucket; - versions.put(selfAddress, localBucket.getVersion()); - } - /** * Returns all the buckets the this node knows about, self owned + remote */ - void receiveGetAllBucket(){ + void receiveGetAllBuckets(){ final ActorRef sender = getSender(); sender.tell(new GetAllBucketsReply(getAllBuckets()), getSelf()); } @@ -153,11 +124,12 @@ public class BucketStore extends AbstractUntypedActorWithMetering { * * @return self owned + remote buckets */ + @SuppressWarnings("rawtypes") Map getAllBuckets(){ Map all = new HashMap<>(remoteBuckets.size() + 1); //first add the local bucket - all.put(selfAddress, localBucket); + all.put(selfAddress, new BucketImpl<>(localBucket)); //then get all remote buckets all.putAll(remoteBuckets); @@ -170,6 +142,7 @@ public class BucketStore extends AbstractUntypedActorWithMetering { * * @param members requested members */ + @SuppressWarnings("rawtypes") void receiveGetBucketsByMembers(Set
members){ final ActorRef sender = getSender(); Map buckets = getBucketsByMembers(members); @@ -182,12 +155,13 @@ public class BucketStore extends AbstractUntypedActorWithMetering { * @param members requested members * @return buckets for requested memebers */ + @SuppressWarnings("rawtypes") Map getBucketsByMembers(Set
members) { Map buckets = new HashMap<>(); //first add the local bucket if asked if (members.contains(selfAddress)) { - buckets.put(selfAddress, localBucket); + buckets.put(selfAddress, new BucketImpl<>(localBucket)); } //then get buckets for requested remote nodes @@ -215,8 +189,9 @@ public class BucketStore extends AbstractUntypedActorWithMetering { * @param receivedBuckets buckets sent by remote * {@link org.opendaylight.controller.remote.rpc.registry.gossip.Gossiper} */ + @SuppressWarnings({ "rawtypes", "unchecked" }) void receiveUpdateRemoteBuckets(Map receivedBuckets){ - + log.debug("{}: receiveUpdateRemoteBuckets: {}", selfAddress, receivedBuckets); if (receivedBuckets == null || receivedBuckets.isEmpty()) { return; //nothing to do @@ -229,10 +204,10 @@ public class BucketStore extends AbstractUntypedActorWithMetering { Long localVersion = versions.get(entry.getKey()); if (localVersion == null) { - localVersion = -1L; + localVersion = NO_VERSION; } - Bucket receivedBucket = entry.getValue(); + Bucket receivedBucket = entry.getValue(); if (receivedBucket == null) { continue; @@ -240,7 +215,7 @@ public class BucketStore extends AbstractUntypedActorWithMetering { Long remoteVersion = receivedBucket.getVersion(); if (remoteVersion == null) { - remoteVersion = -1L; + remoteVersion = NO_VERSION; } //update only if remote version is newer @@ -249,40 +224,27 @@ public class BucketStore extends AbstractUntypedActorWithMetering { versions.put(entry.getKey(), remoteVersion); } } + if(log.isDebugEnabled()) { log.debug("State after update - Local Bucket [{}], Remote Buckets [{}]", localBucket, remoteBuckets); } } - /// - ///Getter Setters - /// - - BucketImpl getLocalBucket() { + protected BucketImpl getLocalBucket() { return localBucket; } - void setLocalBucket(BucketImpl localBucket) { - this.localBucket = localBucket; + protected void updateLocalBucket(T data) { + localBucket.setData(data); + versions.put(selfAddress, localBucket.getVersion()); } - ConcurrentMap getRemoteBuckets() { + protected Map> getRemoteBuckets() { return remoteBuckets; } - void setRemoteBuckets(ConcurrentMap remoteBuckets) { - this.remoteBuckets = remoteBuckets; - } - - ConcurrentMap getVersions() { + @VisibleForTesting + Map getVersions() { return versions; } - - void setVersions(ConcurrentMap versions) { - this.versions = versions; - } - - Address getSelfAddress() { - return selfAddress; - } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java index 4e8f2c61c9..b05bd7d0f6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Messages.java @@ -9,16 +9,14 @@ package org.opendaylight.controller.remote.rpc.registry.gossip; import akka.actor.Address; import com.google.common.base.Preconditions; - import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; - -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBucketVersions; -import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBuckets; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBucketVersions; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.ContainsBuckets; /** @@ -29,46 +27,13 @@ public class Messages { public static class BucketStoreMessages{ - public static class GetLocalBucket implements Serializable { - private static final long serialVersionUID = 1L; - } - - public static class ContainsBucket implements Serializable { - private static final long serialVersionUID = 1L; - final private Bucket bucket; - - public ContainsBucket(Bucket bucket){ - Preconditions.checkArgument(bucket != null, "bucket can not be null"); - this.bucket = bucket; - } - - public Bucket getBucket(){ - return bucket; - } - - } - - public static class UpdateBucket extends ContainsBucket implements Serializable { - private static final long serialVersionUID = 1L; - public UpdateBucket(Bucket bucket){ - super(bucket); - } - } - - public static class GetLocalBucketReply extends ContainsBucket implements Serializable { - private static final long serialVersionUID = 1L; - public GetLocalBucketReply(Bucket bucket){ - super(bucket); - } - } - public static class GetAllBuckets implements Serializable { private static final long serialVersionUID = 1L; } public static class GetBucketsByMembers implements Serializable{ private static final long serialVersionUID = 1L; - private Set
members; + private final Set
members; public GetBucketsByMembers(Set
members){ Preconditions.checkArgument(members != null, "members can not be null"); @@ -82,7 +47,7 @@ public class Messages { public static class ContainsBuckets implements Serializable{ private static final long serialVersionUID = 1L; - private Map buckets; + private final Map buckets; public ContainsBuckets(Map buckets){ Preconditions.checkArgument(buckets != null, "buckets can not be null"); @@ -94,11 +59,12 @@ public class Messages { for (Map.Entry entry : buckets.entrySet()){ //ignore null entries - if ( (entry.getKey() == null) || (entry.getValue() == null) ) + if ( (entry.getKey() == null) || (entry.getValue() == null) ) { continue; + } copy.put(entry.getKey(), entry.getValue()); } - return new HashMap<>(copy); + return copy; } } @@ -162,7 +128,7 @@ public class Messages { public static final class GossipStatus extends ContainsBucketVersions implements Serializable{ private static final long serialVersionUID = 1L; - private Address from; + private final Address from; public GossipStatus(Address from, Map versions) { super(versions); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java index e0d145dbe1..5b7b7e4fdc 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java @@ -1,38 +1,41 @@ package org.opendaylight.controller.remote.rpc.registry; -import akka.actor.ActorPath; import akka.actor.ActorRef; -import akka.actor.ActorSelection; import akka.actor.ActorSystem; -import akka.actor.ChildActorPath; +import akka.actor.Address; import akka.actor.Props; -import akka.pattern.Patterns; +import akka.japi.Pair; import akka.testkit.JavaTestKit; -import akka.util.Timeout; -import com.google.common.base.Predicate; +import com.google.common.util.concurrent.Uninterruptibles; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import javax.annotation.Nullable; import org.junit.After; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.remote.rpc.RemoteRpcProviderConfig; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRoutersReply; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; -import org.opendaylight.controller.remote.rpc.registry.gossip.Messages; +import org.opendaylight.controller.remote.rpc.registry.gossip.Bucket; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBucketsReply; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersions; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersionsReply; import org.opendaylight.controller.sal.connector.api.RpcRouter; -import org.opendaylight.controller.utils.ConditionalProbe; +import org.opendaylight.controller.sal.connector.api.RpcRouter.RouteIdentifier; import org.opendaylight.yangtools.yang.common.QName; -import scala.concurrent.Await; -import scala.concurrent.Future; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; @@ -46,6 +49,8 @@ public class RpcRegistryTest { private ActorRef registry2; private ActorRef registry3; + private int routeIdCounter = 1; + @BeforeClass public static void staticSetup() throws InterruptedException { RemoteRpcProviderConfig config1 = new RemoteRpcProviderConfig.Builder("memberA").build(); @@ -97,27 +102,30 @@ public class RpcRegistryTest { final JavaTestKit mockBroker = new JavaTestKit(node1); - final ActorPath bucketStorePath = new ChildActorPath(registry1.path(), "store"); + Address nodeAddress = node1.provider().getDefaultAddress(); // Add rpc on node 1 registry1.tell(new SetLocalRouter(mockBroker.getRef()), mockBroker.getRef()); - // install probe - final JavaTestKit probe1 = createProbeForMessage(node1, bucketStorePath, - Messages.BucketStoreMessages.UpdateBucket.class); + List> addedRouteIds = createRouteIds(); - registry1.tell(getAddRouteMessage(), mockBroker.getRef()); + registry1.tell(new AddOrUpdateRoutes(addedRouteIds), mockBroker.getRef()); // Bucket store should get an update bucket message. Updated bucket contains added rpc. - probe1.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateBucket.class); + + Map buckets = retrieveBuckets(registry1, mockBroker, nodeAddress); + verifyBucket(buckets.get(nodeAddress), addedRouteIds); + + Map versions = retrieveVersions(registry1, mockBroker); + Assert.assertEquals("Version for bucket " + nodeAddress, buckets.get(nodeAddress).getVersion(), + versions.get(nodeAddress)); // Now remove rpc - registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); + registry1.tell(new RemoveRoutes(addedRouteIds), mockBroker.getRef()); // Bucket store should get an update bucket message. Rpc is removed in the updated bucket - probe1.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateBucket.class); + + verifyEmptyBucket(mockBroker, registry1, nodeAddress); System.out.println("testAddRemoveRpcOnSameNode ending"); @@ -136,30 +144,52 @@ public class RpcRegistryTest { System.out.println("testRpcAddRemoveInCluster starting"); final JavaTestKit mockBroker1 = new JavaTestKit(node1); + final JavaTestKit mockBroker2 = new JavaTestKit(node2); + + List> addedRouteIds = createRouteIds(); - // install probe on node2's bucket store - final ActorPath bucketStorePath = new ChildActorPath(registry2.path(), "store"); - final JavaTestKit probe2 = createProbeForMessage(node2, bucketStorePath, - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + Address node1Address = node1.provider().getDefaultAddress(); // Add rpc on node 1 registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); + registry1.tell(new AddOrUpdateRoutes(addedRouteIds), mockBroker1.getRef()); // Bucket store on node2 should get a message to update its local copy of remote buckets - probe2.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + + Map buckets = retrieveBuckets(registry2, mockBroker2, node1Address); + verifyBucket(buckets.get(node1Address), addedRouteIds); // Now remove - registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); + registry1.tell(new RemoveRoutes(addedRouteIds), mockBroker1.getRef()); - // Bucket store on node2 should get a message to update its local copy of remote buckets - probe2.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + // Bucket store on node2 should get a message to update its local copy of remote buckets. + // Wait for the bucket for node1 to be empty. + + verifyEmptyBucket(mockBroker2, registry2, node1Address); System.out.println("testRpcAddRemoveInCluster ending"); } + private void verifyEmptyBucket(JavaTestKit testKit, ActorRef registry, Address address) + throws AssertionError { + Map buckets; + int nTries = 0; + while(true) { + buckets = retrieveBuckets(registry1, testKit, address); + + try { + verifyBucket(buckets.get(address), Collections.>emptyList()); + break; + } catch (AssertionError e) { + if(++nTries >= 50) { + throw e; + } + } + + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + } + } + /** * Three node cluster. Register rpc on 2 nodes. Ensure 3rd gets updated. * @@ -174,76 +204,142 @@ public class RpcRegistryTest { registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - // install probe on node 3 - final ActorPath bucketStorePath = new ChildActorPath(registry3.path(), "store"); - final JavaTestKit probe3 = createProbeForMessage(node3, bucketStorePath, - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - // Add rpc on node 1 + List> addedRouteIds1 = createRouteIds(); registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - - probe3.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + registry1.tell(new AddOrUpdateRoutes(addedRouteIds1), mockBroker1.getRef()); - // Add same rpc on node 2 + // Add rpc on node 2 + List> addedRouteIds2 = createRouteIds(); registry2.tell(new SetLocalRouter(mockBroker2.getRef()), mockBroker2.getRef()); - registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); + registry2.tell(new AddOrUpdateRoutes(addedRouteIds2), mockBroker2.getRef()); + + Address node1Address = node1.provider().getDefaultAddress(); + Address node2Address = node2.provider().getDefaultAddress(); + + Map buckets = retrieveBuckets(registry3, mockBroker3, node1Address, + node2Address); - probe3.expectMsgClass(FiniteDuration.apply(10, TimeUnit.SECONDS), - Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + verifyBucket(buckets.get(node1Address), addedRouteIds1); + verifyBucket(buckets.get(node2Address), addedRouteIds2); + + Map versions = retrieveVersions(registry3, mockBroker3); + Assert.assertEquals("Version for bucket " + node1Address, buckets.get(node1Address).getVersion(), + versions.get(node1Address)); + Assert.assertEquals("Version for bucket " + node2Address, buckets.get(node2Address).getVersion(), + versions.get(node2Address)); + + RouteIdentifier routeID = addedRouteIds1.get(0); + registry3.tell(new FindRouters(routeID), mockBroker3.getRef()); + + FindRoutersReply reply = mockBroker3.expectMsgClass(Duration.create(3, TimeUnit.SECONDS), + FindRoutersReply.class); + + List> respList = reply.getRouterWithUpdateTime(); + Assert.assertEquals("getRouterWithUpdateTime size", 1, respList.size()); + + respList.get(0).first().tell("hello", ActorRef.noSender()); + mockBroker1.expectMsgEquals(Duration.create(3, TimeUnit.SECONDS), "hello"); } - private JavaTestKit createProbeForMessage(ActorSystem node, ActorPath subjectPath, final Class clazz) - throws Exception { - final JavaTestKit probe = new JavaTestKit(node); - - ConditionalProbe conditionalProbe = new ConditionalProbe(probe.getRef(), new Predicate() { - @Override - public boolean apply(@Nullable Object input) { - if (input != null) { - return clazz.equals(input.getClass()); - } else { - return false; - } + private Map retrieveVersions(ActorRef bucketStore, JavaTestKit testKit) { + bucketStore.tell(new GetBucketVersions(), testKit.getRef()); + GetBucketVersionsReply reply = testKit.expectMsgClass(Duration.create(3, TimeUnit.SECONDS), + GetBucketVersionsReply.class); + return reply.getVersions(); + } + + private void verifyBucket(Bucket bucket, List> expRouteIds) { + RoutingTable table = bucket.getData(); + Assert.assertNotNull("Bucket RoutingTable is null", table); + for(RouteIdentifier r: expRouteIds) { + if(!table.contains(r)) { + Assert.fail("RoutingTable does not contain " + r + ". Actual: " + table); } - }); + } - FiniteDuration duration = Duration.create(3, TimeUnit.SECONDS); - Timeout timeout = new Timeout(duration); - int maxTries = 30; - int i = 0; - while(true) { - ActorSelection subject = node.actorSelection(subjectPath); - Future future = Patterns.ask(subject, conditionalProbe, timeout); + Assert.assertEquals("RoutingTable size", expRouteIds.size(), table.size()); + } - try { - Await.ready(future, duration); - break; - } catch (TimeoutException | InterruptedException e) { - if(++i > maxTries) { - throw e; + private Map retrieveBuckets(ActorRef bucketStore, JavaTestKit testKit, + Address... addresses) { + int nTries = 0; + while(true) { + bucketStore.tell(new GetAllBuckets(), testKit.getRef()); + GetAllBucketsReply reply = testKit.expectMsgClass(Duration.create(3, TimeUnit.SECONDS), + GetAllBucketsReply.class); + + Map buckets = reply.getBuckets(); + boolean foundAll = true; + for(Address addr: addresses) { + Bucket bucket = buckets.get(addr); + if(bucket == null) { + foundAll = false; + break; } } - } - return probe; + if(foundAll) { + return buckets; + } - } + if(++nTries >= 50) { + Assert.fail("Missing expected buckets for addresses: " + Arrays.toString(addresses) + + ", Actual: " + buckets); + } - private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { - return new AddOrUpdateRoutes(createRouteIds()); + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + } } - private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { - return new RemoveRoutes(createRouteIds()); + @SuppressWarnings("unchecked") + @Test + public void testAddRoutesConcurrency() throws Exception { + final JavaTestKit testKit = new JavaTestKit(node1); + + registry1.tell(new SetLocalRouter(testKit.getRef()), ActorRef.noSender()); + + final int nRoutes = 500; + final RouteIdentifier[] added = new RouteIdentifier[nRoutes]; + for(int i = 0; i < nRoutes; i++) { + final RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, + new QName(new URI("/mockrpc"), "type" + i), null); + added[i] = routeId; + + //Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS); + registry1.tell(new AddOrUpdateRoutes(Arrays.>asList(routeId)), + ActorRef.noSender()); + } + + GetAllBuckets getAllBuckets = new GetAllBuckets(); + FiniteDuration duration = Duration.create(3, TimeUnit.SECONDS); + int nTries = 0; + while(true) { + registry1.tell(getAllBuckets, testKit.getRef()); + GetAllBucketsReply reply = testKit.expectMsgClass(duration, GetAllBucketsReply.class); + + Bucket localBucket = reply.getBuckets().values().iterator().next(); + RoutingTable table = localBucket.getData(); + if(table != null && table.size() == nRoutes) { + for(RouteIdentifier r: added) { + Assert.assertEquals("RoutingTable contains " + r, true, table.contains(r)); + } + + break; + } + + if(++nTries >= 50) { + Assert.fail("Expected # routes: " + nRoutes + ", Actual: " + table.size()); + } + + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + } } private List> createRouteIds() throws URISyntaxException { - QName type = new QName(new URI("/mockrpc"), "mockrpc"); + QName type = new QName(new URI("/mockrpc"), "mockrpc" + routeIdCounter++); List> routeIds = new ArrayList<>(); routeIds.add(new RouteIdentifierImpl(null, type, null)); return routeIds; } - } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java index 78fcbd3a14..ddd08a5f47 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java @@ -12,27 +12,23 @@ import akka.actor.Address; import akka.actor.Props; import akka.testkit.TestActorRef; import com.typesafe.config.ConfigFactory; +import java.util.HashMap; +import java.util.Map; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.remote.rpc.TerminationMonitor; -import java.util.HashMap; -import java.util.Map; - public class BucketStoreTest { private static ActorSystem system; - private static BucketStore store; @BeforeClass public static void setup() { system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("unit-test")); system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor"); - - store = createStore(); } @AfterClass @@ -40,21 +36,6 @@ public class BucketStoreTest { system.shutdown(); } - /** - * Given a new local bucket - * Should replace - */ - @Test - public void testReceiveUpdateBucket(){ - Bucket bucket = new BucketImpl(); - Long expectedVersion = bucket.getVersion(); - - store.receiveUpdateBucket(bucket); - - Assert.assertEquals(bucket, store.getLocalBucket()); - Assert.assertEquals(expectedVersion, store.getLocalBucket().getVersion()); - } - /** * Given remote buckets * Should merge with local copy of remote buckets @@ -62,6 +43,8 @@ public class BucketStoreTest { @Test public void testReceiveUpdateRemoteBuckets(){ + BucketStore store = createStore(); + Address localAddress = system.provider().getDefaultAddress(); Bucket localBucket = new BucketImpl(); @@ -84,7 +67,7 @@ public class BucketStoreTest { //Should NOT contain local bucket //Should contain ONLY 3 entries i.e a1, a2, a3 - Map remoteBucketsInStore = store.getRemoteBuckets(); + Map> remoteBucketsInStore = store.getRemoteBuckets(); Assert.assertFalse("remote buckets contains local bucket", remoteBucketsInStore.containsKey(localAddress)); Assert.assertTrue(remoteBucketsInStore.size() == 3); @@ -122,11 +105,9 @@ public class BucketStoreTest { Assert.assertTrue(remoteBucketsInStore.size() == 4); //Should update versions map - //versions map contains versions for all remote buckets (4) + local bucket - //so it should have total 5. + //versions map contains versions for all remote buckets (4). Map versionsInStore = store.getVersions(); - Assert.assertTrue(String.format("Expected:%s, Actual:%s", 5, versionsInStore.size()), - versionsInStore.size() == 5); + Assert.assertEquals(4, versionsInStore.size()); Assert.assertEquals(b1.getVersion(), versionsInStore.get(a1)); Assert.assertEquals(b2.getVersion(), versionsInStore.get(a2)); Assert.assertEquals(b3_new.getVersion(), versionsInStore.get(a3)); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index ded398a33d..e24500a76c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -86,6 +86,7 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.util.EmptyType; +import org.opendaylight.yangtools.yang.model.util.ExtendedType; import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder; import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; @@ -110,7 +111,15 @@ public class RestconfImpl implements RestconfService { } } + private static class TypeDef { + public final TypeDefinition typedef; + public final QName qName; + TypeDef(final TypeDefinition typedef, final QName qName) { + this.typedef = typedef; + this.qName = qName; + } + } private final static RestconfImpl INSTANCE = new RestconfImpl(); @@ -1322,11 +1331,16 @@ public class RestconfImpl implements RestconfService { final DOMMountPoint mountPoint) { final Object value = simpleNode.getValue(); Object inputValue = value; - TypeDefinition typeDefinition = this.typeDefinition(schema); + TypeDef typeDef = this.typeDefinition(schema); + TypeDefinition typeDefinition = typeDef != null ? typeDef.typedef : null; // For leafrefs, extract the type it is pointing to if(typeDefinition instanceof LeafrefTypeDefinition) { - typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema); + if (schema.getQName().equals(typeDef.qName)) { + typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema); + } else { + typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName); + } } if (typeDefinition instanceof IdentityrefTypeDefinition) { @@ -1498,29 +1512,25 @@ public class RestconfImpl implements RestconfService { } } - private TypeDefinition _typeDefinition(final LeafSchemaNode node) { - TypeDefinition baseType = node.getType(); + private TypeDef typeDefinition(final TypeDefinition type, final QName nodeQName) { + TypeDefinition baseType = type; + QName qName = nodeQName; while (baseType.getBaseType() != null) { + if (baseType instanceof ExtendedType) { + qName = baseType.getQName(); + } baseType = baseType.getBaseType(); } - return baseType; - } - - private TypeDefinition typeDefinition(final LeafListSchemaNode node) { - TypeDefinition baseType = node.getType(); - while (baseType.getBaseType() != null) { - baseType = baseType.getBaseType(); - } + return new TypeDef(baseType, qName); - return baseType; } - private TypeDefinition typeDefinition(final DataSchemaNode node) { + private TypeDef typeDefinition(final DataSchemaNode node) { if (node instanceof LeafListSchemaNode) { - return typeDefinition((LeafListSchemaNode) node); + return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName()); } else if (node instanceof LeafSchemaNode) { - return _typeDefinition((LeafSchemaNode) node); + return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName()); } else if (node instanceof AnyXmlSchemaNode) { return null; } else { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonLeafrefToCnSnTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonLeafrefToCnSnTest.java index bdd74e8f96..c11c7dbbe7 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonLeafrefToCnSnTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonLeafrefToCnSnTest.java @@ -24,7 +24,7 @@ public class JsonLeafrefToCnSnTest extends YangAndXmlAndDataSchemaLoader { @BeforeClass public static void initialize() { - dataLoad("/json-to-cnsn/leafref"); + dataLoad("/json-to-cnsn/leafref",2,"leafref-module","cont"); } /** diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/augment-leafref-module b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/augment-leafref-module new file mode 100644 index 0000000000..766cc8153e --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/augment-leafref-module @@ -0,0 +1,21 @@ +module augment-leafref-module { + namespace "augment:leafref:module"; + + prefix "auglfrfmo"; + revision 2014-12-16 { + } + + + typedef leafreftype { + type leafref { + path "/cont/lf3"; + + } + } + + container cont { + leaf lf3 { + type string; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/json/data.json b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/json/data.json index 235666eed4..a9d5d29b44 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/json/data.json +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/json/data.json @@ -1,6 +1,7 @@ { "cont":{ "lf1":121, - "lf2":121 + "lf2":121, + "lf4":"pcc://39.39.39.39" } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/leafref-module b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/leafref-module index 8ca9f09096..9b124a0fe6 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/leafref-module +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/leafref/leafref-module @@ -2,6 +2,8 @@ module leafref-module { namespace "leafref:module"; prefix "lfrfmo"; + + import augment-leafref-module {prefix augleafref; revision-date 2014-12-16;} revision 2013-11-18 { } @@ -14,6 +16,9 @@ module leafref-module { path "/cont/lf1"; } } + leaf lf4 { + type augleafref:leafreftype; + } } } \ No newline at end of file diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsManager.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsManager.java index 751a68965d..6124bdf642 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsManager.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsManager.java @@ -24,6 +24,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103. import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.Queue; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.OpendaylightMeterStatisticsListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener; @@ -53,14 +54,47 @@ public interface StatisticsManager extends AutoCloseable, TransactionChainListen * Internal {@link TransactionChainListener} joining all DS commits * to Set of chained changes for prevent often DataStore touches. */ - public interface StatDataStoreOperation { + public abstract class StatDataStoreOperation { + public enum StatsManagerOperationType { + /** + * Operation will carry out work related to new node addition / + * update + */ + NODE_UPDATE, + /** + * Operation will carry out work related to node removal + */ + NODE_REMOVAL, + /** + * Operation will commit data to the operational data store + */ + DATA_COMMIT_OPER_DS + } + + private NodeId nodeId; + private StatsManagerOperationType operationType = StatsManagerOperationType.DATA_COMMIT_OPER_DS; + + public StatDataStoreOperation(final StatsManagerOperationType operType, final NodeId id){ + if(operType != null){ + operationType = operType; + } + nodeId = id; + } + + public final StatsManagerOperationType getType() { + return operationType; + } + + public final NodeId getNodeId(){ + return nodeId; + } /** - * Apply all read / write (put|merge) operation - * for DataStore + * Apply all read / write (put|merge) operation for DataStore + * * @param {@link ReadWriteTransaction} tx */ - void applyOperation(ReadWriteTransaction tx); + public abstract void applyOperation(ReadWriteTransaction tx); } diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatListenCommitFlow.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatListenCommitFlow.java index e17c45dc76..49fe3bbefd 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatListenCommitFlow.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatListenCommitFlow.java @@ -25,6 +25,7 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.statistics.manager.StatRpcMsgManager.TransactionCacheContainer; import org.opendaylight.controller.md.statistics.manager.StatisticsManager; import org.opendaylight.controller.md.statistics.manager.StatisticsManager.StatDataStoreOperation; +import org.opendaylight.controller.md.statistics.manager.StatisticsManager.StatDataStoreOperation.StatsManagerOperationType; import org.opendaylight.controller.md.statistics.manager.impl.helper.FlowComparator; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; @@ -120,7 +121,7 @@ public class StatListenCommitFlow extends StatAbstractListenCommit> txContainer = getTransactionCacheContainer(transId, nodeId); @@ -218,6 +219,7 @@ public class StatListenCommitFlow extends StatAbstractListenCommit nodeIdent = InstanceIdentifier @@ -157,7 +158,7 @@ public class StatListenCommitGroup extends StatAbstractListenCommit nodeIdent = InstanceIdentifier.create(Nodes.class) .child(Node.class, new NodeKey(nodeId)); /* Don't block RPC Notification thread */ - manager.enqueue(new StatDataStoreOperation() { + manager.enqueue(new StatDataStoreOperation(StatsManagerOperationType.DATA_COMMIT_OPER_DS,nodeId) { @Override public void applyOperation(final ReadWriteTransaction trans) { final Optional> txContainer = getTransactionCacheContainer(transId, nodeId); diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatNotifyCommitTable.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatNotifyCommitTable.java index 53bca87034..2d730645ac 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatNotifyCommitTable.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatNotifyCommitTable.java @@ -17,6 +17,7 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.statistics.manager.StatRpcMsgManager.TransactionCacheContainer; import org.opendaylight.controller.md.statistics.manager.StatisticsManager; import org.opendaylight.controller.md.statistics.manager.StatisticsManager.StatDataStoreOperation; +import org.opendaylight.controller.md.statistics.manager.StatisticsManager.StatDataStoreOperation.StatsManagerOperationType; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; @@ -81,7 +82,7 @@ public class StatNotifyCommitTable extends StatAbstractNotifyCommit tableStats = new ArrayList(10); diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatRpcMsgManagerImpl.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatRpcMsgManagerImpl.java index 4870223c0f..20341bcc66 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatRpcMsgManagerImpl.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatRpcMsgManagerImpl.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.md.statistics.manager.impl; +import java.util.Arrays; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; @@ -50,6 +51,7 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.cache.Cache; @@ -174,13 +176,15 @@ public class StatRpcMsgManagerImpl implements StatRpcMsgManager { @Override public void onSuccess(final RpcResult result) { final TransactionId id = result.getResult().getTransactionId(); + final NodeKey nodeKey = nodeRef.getValue().firstKeyOf(Node.class, NodeKey.class); if (id == null) { - LOG.warn("No protocol support"); + String[] multipartRequestName = result.getResult().getClass().getSimpleName().split("(?=\\p{Upper})"); + LOG.warn("Node [{}] does not support statistics request type : {}", + nodeKey.getId(),Joiner.on(" ").join(Arrays.copyOfRange(multipartRequestName, 2, multipartRequestName.length-2))); } else { if (resultTransId != null) { resultTransId.set(id); } - final NodeKey nodeKey = nodeRef.getValue().firstKeyOf(Node.class, NodeKey.class); final String cacheKey = buildCacheKey(id, nodeKey.getId()); final TransactionCacheContainer container = new TransactionCacheContainerImpl<>(id, inputObj, nodeKey.getId()); diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatisticsManagerImpl.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatisticsManagerImpl.java index 1d03e38c16..437c92f6a0 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatisticsManagerImpl.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/impl/StatisticsManagerImpl.java @@ -29,6 +29,7 @@ import org.opendaylight.controller.md.statistics.manager.StatPermCollector; import org.opendaylight.controller.md.statistics.manager.StatPermCollector.StatCapabTypes; import org.opendaylight.controller.md.statistics.manager.StatRpcMsgManager; import org.opendaylight.controller.md.statistics.manager.StatisticsManager; +import org.opendaylight.controller.md.statistics.manager.StatisticsManager.StatDataStoreOperation.StatsManagerOperationType; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter; @@ -223,7 +224,18 @@ public class StatisticsManagerImpl implements StatisticsManager, Runnable { private synchronized void cleanDataStoreOperQueue() { // Drain all events, making sure any blocked threads are unblocked while (! dataStoreOperQueue.isEmpty()) { - dataStoreOperQueue.poll(); + StatDataStoreOperation op = dataStoreOperQueue.poll(); + + // Execute the node removal clean up operation if queued in the + // operational queue. + if (op.getType() == StatsManagerOperationType.NODE_REMOVAL) { + try { + LOG.debug("Node {} disconnected. Cleaning internal data.",op.getNodeId()); + op.applyOperation(null); + } catch (final Exception ex) { + LOG.warn("Unhandled exception while cleaning up internal data of node [{}]",op.getNodeId()); + } + } } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ServiceRegistryWrapper.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ServiceRegistryWrapper.java index 8c2c74f2ac..9ce550e505 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ServiceRegistryWrapper.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ServiceRegistryWrapper.java @@ -24,24 +24,30 @@ public class ServiceRegistryWrapper { this.configServiceRefRegistry = configServiceRefRegistry; } - public ObjectName getByServiceAndRefName(String namespace, String serviceName, String refName) { - Map> serviceNameToRefNameToInstance = getMappedServices().get(namespace); + public ObjectName getByServiceAndRefName(String namespace, String serviceType, String refName) { + Map>> mappedServices = getMappedServices(); + Map> serviceNameToRefNameToInstance = mappedServices.get(namespace); - Preconditions.checkNotNull(serviceNameToRefNameToInstance, "No serviceInstances mapped to " + namespace); + Preconditions.checkArgument(serviceNameToRefNameToInstance != null, + "No service mapped to %s:%s:%s. Wrong namespace, available namespaces: %s", + namespace, serviceType, refName, mappedServices.keySet()); - Map refNameToInstance = serviceNameToRefNameToInstance.get(serviceName); - Preconditions.checkNotNull(refNameToInstance, "No serviceInstances mapped to " + serviceName + " , " - + serviceNameToRefNameToInstance.keySet()); + Map refNameToInstance = serviceNameToRefNameToInstance.get(serviceType); + Preconditions.checkArgument(refNameToInstance != null, + "No service mapped to %s:%s:%s. Wrong service type, available service types: %s" + , namespace, serviceType, refName, serviceNameToRefNameToInstance.keySet()); String instanceId = refNameToInstance.get(refName); - Preconditions.checkArgument(instanceId != null, "No serviceInstances mapped to " + serviceName + ":" - + refName + ", " + serviceNameToRefNameToInstance.keySet()); + Preconditions.checkArgument(instanceId != null, + "No service mapped to %s:%s:%s. Wrong ref name, available ref names: %s" + ,namespace, serviceType, refName, refNameToInstance.keySet()); Services.ServiceInstance serviceInstance = Services.ServiceInstance.fromString(instanceId); - Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName - + " under service name " + serviceName + " , " + refNameToInstance.keySet()); + Preconditions.checkArgument(serviceInstance != null, + "No service mapped to %s:%s:%s. Wrong ref name, available ref names: %s" + ,namespace, serviceType, refName, refNameToInstance.keySet()); - String qNameOfService = configServiceRefRegistry.getServiceInterfaceName(namespace, serviceName); + String qNameOfService = configServiceRefRegistry.getServiceInterfaceName(namespace, serviceType); try { /* Remove transaction name as this is redundant - will be stripped in DynamicWritableWrapper, @@ -51,7 +57,7 @@ public class ServiceRegistryWrapper { configServiceRefRegistry.getServiceReference(qNameOfService, refName)); } catch (InstanceNotFoundException e) { throw new IllegalArgumentException("No serviceInstance mapped to " + refName - + " under service name " + serviceName + " , " + refNameToInstance.keySet(), e); + + " under service name " + serviceType + " , " + refNameToInstance.keySet(), e); } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java new file mode 100644 index 0000000000..ea019642c5 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Lock.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. 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.controller.netconf.confignetconfconnector.operations; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; +import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException; +import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Simple Lock implementation that pretends to lock candidate datastore. + * Candidate datastore is allocated per session and is private so no real locking is needed (JMX is the only possible interference) + */ +public class Lock extends AbstractLastNetconfOperation { + + private static final Logger LOG = LoggerFactory.getLogger(Lock.class); + + private static final String LOCK = "lock"; + private static final String TARGET_KEY = "target"; + + public Lock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = extractTargetParameter(operationElement); + if(targetDatastore == Datastore.candidate) { + // Since candidate datastore instances are allocated per session and not accessible anywhere else, no need to lock + LOG.debug("Locking {} datastore on session: {}", targetDatastore, getNetconfSessionIdForReporting()); + // TODO should this fail if we are already locked ? + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + // Not supported running lock + throw new NetconfDocumentedException("Unable to lock " + Datastore.running + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + static Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException { + final XmlElement targetChildNode; + try { + final XmlElement targetElement = operationElement.getOnlyChildElementWithSameNamespace(TARGET_KEY); + targetChildNode = targetElement.getOnlyChildElementWithSameNamespace(); + } catch (final MissingNameSpaceException | UnexpectedNamespaceException e) { + LOG.trace("Can't get only child element with same namespace", e); + throw NetconfDocumentedException.wrap(e); + } + + return Datastore.valueOf(targetChildNode.getName()); + } + + @Override + protected String getOperationName() { + return LOCK; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java new file mode 100644 index 0000000000..07b10aa327 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/UnLock.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. 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.controller.netconf.confignetconfconnector.operations; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Simple unlock implementation that pretends to unlock candidate datastore. + * Candidate datastore is allocated per session and is private so no real locking is needed (JMX is the only possible interference) + */ +public class UnLock extends AbstractLastNetconfOperation { + + private static final Logger LOG = LoggerFactory.getLogger(UnLock.class); + + private static final String UNLOCK = "unlock"; + + public UnLock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = Lock.extractTargetParameter(operationElement); + if(targetDatastore == Datastore.candidate) { + // Since candidate datastore instances are allocated per session and not accessible anywhere else, no need to lock + LOG.debug("Unlocking {} datastore on session: {}", targetDatastore, getNetconfSessionIdForReporting()); + // TODO this should fail if we are not locked + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + // Not supported running lock + throw new NetconfDocumentedException("Unable to unlock " + Datastore.running + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + @Override + protected String getOperationName() { + return UNLOCK; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java index 294e0c8013..04d5d4bb6f 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java @@ -14,6 +14,8 @@ import java.util.Set; import org.opendaylight.controller.config.util.ConfigRegistryClient; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Lock; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.UnLock; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Validate; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; @@ -48,6 +50,8 @@ final class NetconfOperationProvider { ops.add(new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new Lock(netconfSessionIdForReporting)); + ops.add(new UnLock(netconfSessionIdForReporting)); ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index beb3365f1c..6f9a62af1a 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -83,6 +83,8 @@ import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Lock; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.UnLock; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig; @@ -96,6 +98,7 @@ import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnap import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity1; @@ -236,6 +239,12 @@ public class NetconfMappingTest extends AbstractConfigTest { } + @Test + public void testUnLock() throws Exception { + assertTrue(NetconfMessageUtil.isOKMessage(lockCandidate())); + assertTrue(NetconfMessageUtil.isOKMessage(unlockCandidate())); + } + private void assertCorrectRefNamesForDependencies(Document config) throws NodeTestException { NodeList modulesList = config.getElementsByTagName("modules"); assertEquals(1, modulesList.getLength()); @@ -383,6 +392,16 @@ public class NetconfMappingTest extends AbstractConfigTest { executeOp(commitOp, "netconfMessages/commit.xml"); } + private Document lockCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + Lock commitOp = new Lock(NETCONF_SESSION_ID); + return executeOp(commitOp, "netconfMessages/lock.xml"); + } + + private Document unlockCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + UnLock commitOp = new UnLock(NETCONF_SESSION_ID); + return executeOp(commitOp, "netconfMessages/unlock.xml"); + } + private Document getConfigCandidate() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional. absent(), transactionProvider, diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java index 65810a6bda..fd362f83e7 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java @@ -34,6 +34,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; @@ -77,6 +78,7 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { public static final String LOOPBACK_ADDRESS = "127.0.0.1"; public static final int SERVER_CONNECTION_TIMEOUT_MILLIS = 5000; + private static final int RESOURCE_TIMEOUT_MINUTES = 2; static ModuleFactory[] FACTORIES = {new TestImplModuleFactory(), new DepTestImplModuleFactory(), @@ -145,7 +147,7 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { } else { s = dispatch.createServer(((InetSocketAddress) getTcpServerAddress())); } - s.await(); + s.await(RESOURCE_TIMEOUT_MINUTES, TimeUnit.MINUTES); return s.channel(); } @@ -230,9 +232,9 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { */ @After public void cleanUpNetconf() throws Exception { - serverTcpChannel.close().await(); + serverTcpChannel.close().await(RESOURCE_TIMEOUT_MINUTES, TimeUnit.MINUTES); hashedWheelTimer.stop(); - nettyThreadgroup.shutdownGracefully().await(); + nettyThreadgroup.shutdownGracefully().await(RESOURCE_TIMEOUT_MINUTES, TimeUnit.MINUTES); } public NetconfClientConfiguration getClientConfiguration(final InetSocketAddress tcpAddress, final int timeout) { diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index bd8e2ed4df..7d568b6462 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -106,12 +106,12 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { @After public void tearDown() throws Exception { sshProxyServer.close(); - clientGroup.shutdownGracefully().await(); + clientGroup.shutdownGracefully(); minaTimerEx.shutdownNow(); nioExec.shutdownNow(); } - @Test + @Test(timeout = 2*60*1000) public void testSecure() throws Exception { final NetconfClientDispatcher dispatch = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration(new SimpleNetconfClientSessionListener(), TLS_ADDRESS))) { diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml new file mode 100644 index 0000000000..f5228b2e94 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/lock.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml new file mode 100644 index 0000000000..e6e3770ae6 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unlock.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file