Merge "Remove null check because of guarantee of non-null"
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
index cffdc09..3ae5b82 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
@@ -598,7 +598,7 @@
Set<ConnectPoint> ingressPorts = new HashSet<>();
for (Interface intf : interfaceService.getInterfaces()) {
- if (!intf.equals(egressInterface)) {
+ if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
ConnectPoint srcPort = intf.connectPoint();
ingressPorts.add(srcPort);
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java b/core/api/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
similarity index 100%
rename from core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
rename to core/api/src/test/java/org/onlab/onos/net/flow/DefaultFlowEntryTest.java
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java b/core/api/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
similarity index 100%
rename from core/net/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
rename to core/api/src/test/java/org/onlab/onos/net/flow/DefaultFlowRuleTest.java
diff --git a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
similarity index 100%
rename from core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
rename to core/api/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java
index dc913bf..97cf50e 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocol.java
@@ -1,6 +1,5 @@
package org.onlab.onos.store.service.impl;
-import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.ArrayList;
@@ -38,7 +37,6 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.cluster.ClusterService;
-import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
import org.onlab.onos.store.serializers.ImmutableListSerializer;
@@ -172,20 +170,9 @@
@Override
public ProtocolClient createClient(TcpMember member) {
- ControllerNode remoteNode = getControllerNode(member.host(), member.port());
- checkNotNull(remoteNode,
- "No matching ONOS Node for %s:%s",
- member.host(), member.port());
- return new ClusterMessagingProtocolClient(
- clusterCommunicator, clusterService.getLocalNode(), remoteNode);
- }
-
- private ControllerNode getControllerNode(String host, int port) {
- for (ControllerNode node : clusterService.getNodes()) {
- if (node.ip().toString().equals(host) && node.tcpPort() == port) {
- return node;
- }
- }
- return null;
+ return new ClusterMessagingProtocolClient(clusterService,
+ clusterCommunicator,
+ clusterService.getLocalNode(),
+ member);
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java
index 23c34b2..3dd93b9 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/ClusterMessagingProtocolClient.java
@@ -13,6 +13,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import net.kuujo.copycat.cluster.TcpMember;
import net.kuujo.copycat.protocol.PingRequest;
import net.kuujo.copycat.protocol.PingResponse;
import net.kuujo.copycat.protocol.PollRequest;
@@ -23,6 +24,9 @@
import net.kuujo.copycat.protocol.SyncResponse;
import net.kuujo.copycat.spi.protocol.ProtocolClient;
+import org.onlab.onos.cluster.ClusterEvent;
+import org.onlab.onos.cluster.ClusterEventListener;
+import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
@@ -43,21 +47,30 @@
public static final long RETRY_INTERVAL_MILLIS = 2000;
+ private final ClusterService clusterService;
private final ClusterCommunicationService clusterCommunicator;
private final ControllerNode localNode;
- private final ControllerNode remoteNode;
+ private final TcpMember remoteMember;
+ private ControllerNode remoteNode;
// FIXME: Thread pool sizing.
private static final ScheduledExecutorService THREAD_POOL =
new ScheduledThreadPoolExecutor(10, THREAD_FACTORY);
+ private volatile CompletableFuture<Void> appeared;
+
+ private volatile InternalClusterEventListener listener;
+
public ClusterMessagingProtocolClient(
+ ClusterService clusterService,
ClusterCommunicationService clusterCommunicator,
ControllerNode localNode,
- ControllerNode remoteNode) {
+ TcpMember remoteMember) {
+
+ this.clusterService = clusterService;
this.clusterCommunicator = clusterCommunicator;
this.localNode = localNode;
- this.remoteNode = remoteNode;
+ this.remoteMember = remoteMember;
}
@Override
@@ -81,15 +94,64 @@
}
@Override
- public CompletableFuture<Void> connect() {
- return CompletableFuture.completedFuture(null);
+ public synchronized CompletableFuture<Void> connect() {
+ if (remoteNode != null) {
+ // done
+ return CompletableFuture.completedFuture(null);
+ }
+
+ remoteNode = getControllerNode(remoteMember);
+
+ if (remoteNode != null) {
+ // done
+ return CompletableFuture.completedFuture(null);
+ }
+
+ if (appeared != null) {
+ // already waiting for member to appear
+ return appeared;
+ }
+
+ appeared = new CompletableFuture<>();
+ listener = new InternalClusterEventListener();
+ clusterService.addListener(listener);
+
+ // wait for specified controller node to come up
+ return null;
}
@Override
- public CompletableFuture<Void> close() {
+ public synchronized CompletableFuture<Void> close() {
+ if (listener != null) {
+ clusterService.removeListener(listener);
+ listener = null;
+ }
+ if (appeared != null) {
+ appeared.cancel(true);
+ appeared = null;
+ }
return CompletableFuture.completedFuture(null);
}
+ private synchronized void checkIfMemberAppeared() {
+ final ControllerNode controllerNode = getControllerNode(remoteMember);
+ if (controllerNode == null) {
+ // still not there: no-op
+ return;
+ }
+
+ // found
+ remoteNode = controllerNode;
+ if (appeared != null) {
+ appeared.complete(null);
+ }
+
+ if (listener != null) {
+ clusterService.removeListener(listener);
+ listener = null;
+ }
+ }
+
private <I> MessageSubject messageType(I input) {
Class<?> clazz = input.getClass();
if (clazz.equals(PollRequest.class)) {
@@ -112,6 +174,30 @@
return future;
}
+ private ControllerNode getControllerNode(TcpMember remoteMember) {
+ final String host = remoteMember.host();
+ final int port = remoteMember.port();
+ for (ControllerNode node : clusterService.getNodes()) {
+ if (node.ip().toString().equals(host) && node.tcpPort() == port) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private final class InternalClusterEventListener
+ implements ClusterEventListener {
+
+ public InternalClusterEventListener() {
+ }
+
+ @Override
+ public void event(ClusterEvent event) {
+ checkIfMemberAppeared();
+ }
+
+ }
+
private class RPCTask<I, O> implements Runnable {
private final I request;
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex2.json b/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex2.json
new file mode 100644
index 0000000..2a05249
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex2.json
@@ -0,0 +1,11 @@
+{
+ "event": "showPath",
+ "sid": 3,
+ "payload": {
+ "ids": [
+ "of:0000000000000007"
+ ],
+ "traffic": true
+ }
+}
+// what is the client supposed to do with this?
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex1_devs.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex1_devs.json
new file mode 100644
index 0000000..725c15f
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex1_devs.json
@@ -0,0 +1,11 @@
+{
+ "event": "requestTraffic",
+ "sid": 6,
+ "payload": {
+ "ids": [
+ "of:0000000000000007",
+ "of:000000000000000c",
+ "of:000000000000000a"
+ ]
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex2_hosts.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex2_hosts.json
new file mode 100644
index 0000000..84f17df
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex2_hosts.json
@@ -0,0 +1,12 @@
+{
+ "event": "requestTraffic",
+ "sid": 12,
+ "payload": {
+ "ids": [
+ "86:C3:7B:90:79:CD/-1",
+ "22:BA:28:81:FD:45/-1",
+ "BA:91:F6:8E:B6:B6/-1",
+ "06:E2:E6:F7:03:12/-1"
+ ]
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex3_devs_hosts.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex3_devs_hosts.json
new file mode 100644
index 0000000..3f915df
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestTraffic_ex3_devs_hosts.json
@@ -0,0 +1,12 @@
+{
+ "event": "requestTraffic",
+ "sid": 18,
+ "payload": {
+ "ids": [
+ "of:0000000000000001",
+ "86:C3:7B:90:79:CD/-1",
+ "7E:D2:EE:0F:12:4A/-1",
+ "of:000000000000000c"
+ ]
+ }
+}
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
index 0644c32..7632148 100644
--- a/web/gui/src/main/webapp/onos2.js
+++ b/web/gui/src/main/webapp/onos2.js
@@ -115,7 +115,6 @@
}
function doError(msg) {
- errorCount++;
console.error(msg);
doAlert(msg);
}
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 0dfa466..a8c67a1 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -139,6 +139,8 @@
#topo-detail td.value {
}
+
+
#topo-detail hr {
height: 1px;
color: #ccc;
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 48e4851..8a3bc5d 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -127,7 +127,8 @@
P: togglePorts,
U: unpin,
- Z: requestPath,
+ W: requestTraffic, // bag of selections
+ Z: requestPath, // host-to-host intent (and monitor)
X: cancelMonitor
};
@@ -199,7 +200,7 @@
function abortIfLive() {
if (config.useLiveData) {
- scenario.view.alert("Sorry, currently using live data..");
+ network.view.alert("Sorry, currently using live data..");
return true;
}
return false;
@@ -342,14 +343,18 @@
addDevice: addDevice,
addLink: addLink,
addHost: addHost,
+
updateDevice: updateDevice,
updateLink: updateLink,
updateHost: updateHost,
+
removeDevice: stillToImplement,
removeLink: removeLink,
removeHost: removeHost,
+
showDetails: showDetails,
- showPath: showPath
+ showPath: showPath,
+ showTraffic: showTraffic
};
function addDevice(data) {
@@ -462,6 +467,7 @@
function showDetails(data) {
fnTrace('showDetails', data.payload.id);
populateDetails(data.payload);
+ // TODO: Add single-select actions ...
detailPane.show();
}
@@ -484,6 +490,10 @@
// TODO: add selection-highlite lines to links
}
+ function showTraffic(data) {
+ network.view.alert("showTraffic() -- TODO")
+ }
+
// ...............................
function stillToImplement(data) {
@@ -504,24 +514,58 @@
// ==============================
// Out-going messages...
+ function userFeedback(msg) {
+ // for now, use the alert pane as is. Maybe different alert style in
+ // the future (centered on view; dismiss button?)
+ network.view.alert(msg);
+ }
+
+ function nSel() {
+ return selectOrder.length;
+ }
function getSel(idx) {
return selections[selectOrder[idx]];
}
+ function getSelId(idx) {
+ return getSel(idx).obj.id;
+ }
+ function allSelectionsClass(cls) {
+ for (var i=0, n=nSel(); i<n; i++) {
+ if (getSel(i).obj.class !== cls) {
+ return false;
+ }
+ }
+ return true;
+ }
- // for now, just a host-to-host intent, (and implicit start-monitoring)
+ function requestTraffic() {
+ if (nSel() > 0) {
+ sendMessage('requestTraffic', {
+ ids: selectOrder
+ });
+ } else {
+ userFeedback('Request-Traffic requires one or\n' +
+ 'more items to be selected.');
+ }
+ }
+
function requestPath() {
- var payload = {
- one: getSel(0).obj.id,
- two: getSel(1).obj.id
- };
- sendMessage('requestPath', payload);
+ if (nSel() === 2 && allSelectionsClass('host')) {
+ sendMessage('requestPath', {
+ one: getSelId(0),
+ two: getSelId(1)
+ });
+ } else {
+ userFeedback('Request-Path requires two\n' +
+ 'hosts to be selected.');
+ }
}
function cancelMonitor() {
- var payload = {
- id: "need_the_intent_id" // FIXME: where are we storing this?
- };
- sendMessage('cancelMonitor', payload);
+ // FIXME: from where do we get the intent id(s) to send to the server?
+ sendMessage('cancelMonitor', {
+ ids: ["need_the_intent_id"]
+ });
}
// request details for the selected element
@@ -1200,12 +1244,43 @@
function singleSelect() {
requestDetails();
- // NOTE: detail pane will be shown from showDetails event.
+ // NOTE: detail pane will be shown from showDetails event callback
}
function multiSelect() {
- // TODO: use detail pane for multi-select view.
- //detailPane.show();
+ populateMultiSelect();
+ // TODO: Add multi-select actions ...
+ }
+
+ function addSep(tbody) {
+ var tr = tbody.append('tr');
+ $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
+ }
+
+ function addProp(tbody, label, value) {
+ var tr = tbody.append('tr');
+
+ tr.append('td')
+ .attr('class', 'label')
+ .text(label + ' :');
+
+ tr.append('td')
+ .attr('class', 'value')
+ .text(value);
+ }
+
+ function populateMultiSelect() {
+ detailPane.empty();
+
+ var title = detailPane.append("h2"),
+ table = detailPane.append("table"),
+ tbody = table.append("tbody");
+
+ title.text('Multi-Select...');
+
+ selectOrder.forEach(function (d, i) {
+ addProp(tbody, i+1, d);
+ });
}
function populateDetails(data) {
@@ -1225,23 +1300,6 @@
addProp(tbody, p, data.props[p]);
}
});
-
- function addSep(tbody) {
- var tr = tbody.append('tr');
- $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
- }
-
- function addProp(tbody, label, value) {
- var tr = tbody.append('tr');
-
- tr.append('td')
- .attr('class', 'label')
- .text(label + ' :');
-
- tr.append('td')
- .attr('class', 'value')
- .text(value);
- }
}
// ==============================