Implement east-west communication for k8s network
Change-Id: Ibac91b7a856e35a26cf0e0f23d6d01f65197625d
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
index eb3592f..7526fdd 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
@@ -31,6 +31,7 @@
public static final String ARP_PROXY_MODE = "proxy";
public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02";
+ public static final String DEFAULT_HOST_MAC_STR = "fe:00:00:00:00:08";
public static final MacAddress DEFAULT_GATEWAY_MAC =
MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
@@ -43,6 +44,7 @@
// flow priority
public static final int PRIORITY_SNAT_RULE = 26000;
+ public static final int PRIORITY_TUNNEL_TAG_RULE = 30000;
public static final int PRIORITY_DHCP_RULE = 42000;
public static final int PRIORITY_ADMIN_RULE = 32000;
public static final int PRIORITY_ACL_RULE = 31000;
@@ -50,6 +52,7 @@
public static final int PRIORITY_CT_HOOK_RULE = 30500;
public static final int PRIORITY_CT_RULE = 32000;
public static final int PRIORITY_CT_DROP_RULE = 32500;
+ public static final int PRIORITY_SWITCHING_RULE = 30000;
public static final int PRIORITY_ARP_GATEWAY_RULE = 41000;
public static final int PRIORITY_ARP_SUBNET_RULE = 40000;
public static final int PRIORITY_ARP_CONTROL_RULE = 40000;
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/DefaultK8sPort.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/DefaultK8sPort.java
index 84548a2..4d2b906 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/DefaultK8sPort.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/DefaultK8sPort.java
@@ -101,6 +101,19 @@
}
@Override
+ public K8sPort updatePortNumber(PortNumber portNumber) {
+ return new Builder()
+ .networkId(networkId)
+ .portId(portId)
+ .macAddress(macAddress)
+ .ipAddress(ipAddress)
+ .deviceId(deviceId)
+ .portNumber(portNumber)
+ .state(state)
+ .build();
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) {
return true;
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sNetworkEvent.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sNetworkEvent.java
index a39beca..d2964a2 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sNetworkEvent.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sNetworkEvent.java
@@ -61,6 +61,16 @@
* Signifies that the kubernetes port is removed.
*/
K8S_PORT_REMOVED,
+
+ /**
+ * Signifies that the kubernetes port is activated.
+ */
+ K8S_PORT_ACTIVATED,
+
+ /**
+ * Signifies that the kubernetes port is inactivated.
+ */
+ K8S_PORT_INACTIVATED,
}
/**
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sPort.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sPort.java
index 13f5c47..f25a725 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sPort.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/K8sPort.java
@@ -99,6 +99,14 @@
K8sPort updateState(State newState);
/**
+ * Returns new port instance with the given port number.
+ *
+ * @param portNumber updated port number
+ * @return updated port
+ */
+ K8sPort updatePortNumber(PortNumber portNumber);
+
+ /**
* Builder of new port.
*/
interface Builder {
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/cli/K8sPortListCommand.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/cli/K8sPortListCommand.java
index 307736b..28cd8a7 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/cli/K8sPortListCommand.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/cli/K8sPortListCommand.java
@@ -63,7 +63,7 @@
for (K8sPort port: ports) {
K8sNetwork k8sNet = service.network(port.networkId());
print(FORMAT, port.portId(),
- k8sNet.name(),
+ k8sNet == null ? "" : k8sNet.name(),
port.macAddress(),
port.ipAddress() == null ? "" : port.ipAddress().toString());
}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/codec/K8sPortCodec.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/codec/K8sPortCodec.java
index 6b20a59..debc419 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/codec/K8sPortCodec.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/codec/K8sPortCodec.java
@@ -108,6 +108,8 @@
JsonNode stateJson = json.get(STATE);
if (stateJson != null) {
builder.state(State.valueOf(stateJson.asText()));
+ } else {
+ builder.state(State.INACTIVE);
}
return builder.build();
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sNetworkStore.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sNetworkStore.java
index 8e54bef..9117e31 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sNetworkStore.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sNetworkStore.java
@@ -51,9 +51,13 @@
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_NETWORK_CREATED;
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_NETWORK_REMOVED;
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_NETWORK_UPDATED;
+import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_PORT_ACTIVATED;
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_PORT_CREATED;
+import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_PORT_INACTIVATED;
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_PORT_REMOVED;
import static org.onosproject.k8snetworking.api.K8sNetworkEvent.Type.K8S_PORT_UPDATED;
+import static org.onosproject.k8snetworking.api.K8sPort.State.ACTIVE;
+import static org.onosproject.k8snetworking.api.K8sPort.State.INACTIVE;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -254,11 +258,7 @@
break;
case UPDATE:
log.debug("Kubernetes port updated");
- eventExecutor.execute(() ->
- notifyDelegate(new K8sNetworkEvent(
- K8S_PORT_UPDATED,
- network(event.newValue().value().networkId()),
- event.newValue().value())));
+ eventExecutor.execute(() -> processPortUpdate(event));
break;
case REMOVE:
log.debug("Kubernetes port removed");
@@ -272,5 +272,30 @@
break;
}
}
+
+ private void processPortUpdate(MapEvent<String, K8sPort> event) {
+ K8sPort.State oldState = event.oldValue().value().state();
+ K8sPort.State newState = event.newValue().value().state();
+
+ eventExecutor.execute(() ->
+ notifyDelegate(new K8sNetworkEvent(
+ K8S_PORT_UPDATED,
+ network(event.newValue().value().networkId()),
+ event.newValue().value())));
+
+ if (oldState == INACTIVE && newState == ACTIVE) {
+ notifyDelegate(new K8sNetworkEvent(
+ K8S_PORT_ACTIVATED,
+ network(event.newValue().value().networkId()),
+ event.newValue().value()));
+ }
+
+ if (oldState == ACTIVE && newState == INACTIVE) {
+ notifyDelegate(new K8sNetworkEvent(
+ K8S_PORT_INACTIVATED,
+ network(event.newValue().value().networkId()),
+ event.newValue().value()));
+ }
+ }
}
}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
index 7cdf44e..52844d5 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
@@ -15,17 +15,24 @@
*/
package org.onosproject.k8snetworking.impl;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipService;
import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.k8snetworking.api.K8sFlowRuleService;
+import org.onosproject.k8snetworking.api.K8sNetwork;
+import org.onosproject.k8snetworking.api.K8sNetworkEvent;
+import org.onosproject.k8snetworking.api.K8sNetworkListener;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
import org.onosproject.k8snode.api.K8sNode;
import org.onosproject.k8snode.api.K8sNodeEvent;
import org.onosproject.k8snode.api.K8sNodeListener;
import org.onosproject.k8snode.api.K8sNodeService;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -48,7 +55,6 @@
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
-import static org.onosproject.k8snetworking.api.Constants.ACL_INGRESS_TABLE;
import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
import static org.onosproject.k8snetworking.api.Constants.DHCP_TABLE;
@@ -89,11 +95,15 @@
protected LeadershipService leadershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sNetworkService k8sNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected K8sNodeService k8sNodeService;
private final ExecutorService deviceEventExecutor =
Executors.newSingleThreadExecutor(groupedThreads(
getClass().getSimpleName(), "device-event"));
+ private final K8sNetworkListener internalNetworkListener = new InternalK8sNetworkListener();
private final K8sNodeListener internalNodeListener = new InternalK8sNodeListener();
private ApplicationId appId;
@@ -104,10 +114,10 @@
appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
coreService.registerApplication(K8S_NETWORKING_APP_ID);
k8sNodeService.addListener(internalNodeListener);
+ k8sNetworkService.addListener(internalNetworkListener);
localNodeId = clusterService.getLocalNode().id();
leadershipService.runForLeadership(appId.name());
- k8sNodeService.completeNodes().forEach(node ->
- initializePipeline(node.intgBridge()));
+ k8sNodeService.completeNodes().forEach(this::initializePipeline);
log.info("Started");
}
@@ -115,6 +125,7 @@
@Deactivate
protected void deactivate() {
k8sNodeService.removeListener(internalNodeListener);
+ k8sNetworkService.removeListener(internalNetworkListener);
leadershipService.withdraw(appId.name());
deviceEventExecutor.shutdown();
@@ -200,7 +211,10 @@
}));
}
- protected void initializePipeline(DeviceId deviceId) {
+ protected void initializePipeline(K8sNode k8sNode) {
+
+ DeviceId deviceId = k8sNode.intgBridge();
+
// for inbound table transition
connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
connectTables(deviceId, VTAP_INBOUND_TABLE, DHCP_TABLE);
@@ -211,24 +225,25 @@
// for vTag and ARP table transition
connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
- // for ARP and ACL table transition
- connectTables(deviceId, ARP_TABLE, ACL_INGRESS_TABLE);
-
- // for ACL and JUMP table transition
connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
+ // for ARP and ACL table transition
+ connectTables(deviceId, ARP_TABLE, JUMP_TABLE);
+
// for JUMP table transition
// we need JUMP table for bypassing routing table which contains large
// amount of flow rules which might cause performance degradation during
// table lookup
- setupJumpTable(deviceId);
+ setupJumpTable(k8sNode);
// for outbound table transition
connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
}
- private void setupJumpTable(DeviceId deviceId) {
+ private void setupJumpTable(K8sNode k8sNode) {
+ DeviceId deviceId = k8sNode.intgBridge();
+
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
@@ -265,6 +280,50 @@
applyRule(flowRule, true);
}
+ private void setupHostGwRule(K8sNetwork k8sNetwork) {
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32));
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(PortNumber.LOCAL);
+
+ for (K8sNode node : k8sNodeService.completeNodes()) {
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(node.intgBridge())
+ .withSelector(sBuilder.build())
+ .withTreatment(tBuilder.build())
+ .withPriority(HIGH_PRIORITY)
+ .fromApp(appId)
+ .makePermanent()
+ .forTable(JUMP_TABLE)
+ .build();
+ applyRule(flowRule, true);
+ }
+
+ sBuilder = DefaultTrafficSelector.builder();
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32))
+ .matchIPDst(IpPrefix.valueOf(k8sNetwork.cidr()));
+
+ tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
+ .transition(STAT_OUTBOUND_TABLE);
+
+ for (K8sNode node : k8sNodeService.completeNodes()) {
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(node.intgBridge())
+ .withSelector(sBuilder.build())
+ .withTreatment(tBuilder.build())
+ .withPriority(HIGH_PRIORITY)
+ .fromApp(appId)
+ .makePermanent()
+ .forTable(JUMP_TABLE)
+ .build();
+ applyRule(flowRule, true);
+ }
+ }
+
private class InternalK8sNodeListener implements K8sNodeListener {
private boolean isRelevantHelper() {
return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
@@ -283,7 +342,7 @@
return;
}
- initializePipeline(k8sNode.intgBridge());
+ initializePipeline(k8sNode);
});
break;
case K8S_NODE_CREATED:
@@ -293,4 +352,33 @@
}
}
}
+
+ private class InternalK8sNetworkListener implements K8sNetworkListener {
+
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+ }
+
+ @Override
+ public void event(K8sNetworkEvent event) {
+
+ switch (event.type()) {
+ case K8S_NETWORK_CREATED:
+ deviceEventExecutor.execute(() -> processNetworkCreation(event.subject()));
+ break;
+ case K8S_NETWORK_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void processNetworkCreation(K8sNetwork network) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ setupHostGwRule(network);
+ }
+ }
}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java
new file mode 100644
index 0000000..90ad174
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snetworking.impl;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snetworking.api.K8sFlowRuleService;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snetworking.api.K8sPort;
+import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeEvent;
+import org.onosproject.k8snode.api.K8sNodeListener;
+import org.onosproject.k8snode.api.K8sNodeService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
+import static org.onosproject.k8snetworking.api.Constants.ARP_PROXY_MODE;
+import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
+import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE;
+import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
+import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
+import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
+
+/**
+ * Handles ARP packet from containers.
+ */
+@Component(
+ immediate = true,
+ property = {
+ GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
+ ARP_MODE + "=" + ARP_MODE_DEFAULT
+ }
+)
+public class K8sSwitchingArpHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ComponentConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sNodeService k8sNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sNetworkService k8sNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sFlowRuleService k8sFlowRuleService;
+
+ /** Fake MAC address for virtual network subnet gateway. */
+ private String gatewayMac = GATEWAY_MAC_DEFAULT;
+
+ /** ARP processing mode, broadcast | proxy (default). */
+ protected String arpMode = ARP_MODE_DEFAULT;
+
+ private MacAddress gwMacAddress;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+ private final InternalNodeEventListener k8sNodeListener = new InternalNodeEventListener();
+
+ private ApplicationId appId;
+ private NodeId localNodeId;
+
+ @Activate
+ void activate() {
+ appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
+ configService.registerProperties(getClass());
+ localNodeId = clusterService.getLocalNode().id();
+ leadershipService.runForLeadership(appId.name());
+ k8sNodeService.addListener(k8sNodeListener);
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ k8sNodeService.removeListener(k8sNodeListener);
+ leadershipService.withdraw(appId.name());
+ configService.unregisterProperties(getClass(), false);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ void modified(ComponentContext context) {
+ readComponentConfiguration(context);
+
+ log.info("Modified");
+ }
+
+ /**
+ * Processes ARP request packets.
+ *
+ * @param context packet context
+ * @param ethPacket ethernet packet
+ */
+ private void processPacketIn(PacketContext context, Ethernet ethPacket) {
+ // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
+ if (ARP_BROADCAST_MODE.equals(getArpMode())) {
+ return;
+ }
+
+ ARP arpPacket = (ARP) ethPacket.getPayload();
+ if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
+ return;
+ }
+
+ K8sPort srcPort = k8sNetworkService.ports().stream()
+ .filter(p -> p.macAddress().equals(ethPacket.getSourceMAC()))
+ .findAny().orElse(null);
+
+ if (srcPort == null && !context.inPacket().receivedFrom().port()
+ .equals(PortNumber.LOCAL)) {
+ log.warn("Failed to find source port(MAC:{})", ethPacket.getSourceMAC());
+ return;
+ }
+
+ // FIXME: this is a workaround for storing host GW MAC address,
+ // need to find a way to store the MAC address in persistent way
+ if (context.inPacket().receivedFrom().port().equals(PortNumber.LOCAL)) {
+ gwMacAddress = ethPacket.getSourceMAC();
+ }
+
+ IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+
+ MacAddress replyMac = k8sNetworkService.ports().stream()
+ // .filter(p -> p.networkId().equals(srcPort.networkId()))
+ .filter(p -> p.ipAddress().equals(targetIp))
+ .map(K8sPort::macAddress)
+ .findAny().orElse(null);
+
+ long gwIpCnt = k8sNetworkService.networks().stream()
+ .filter(n -> n.gatewayIp().equals(targetIp))
+ .count();
+
+ if (gwIpCnt > 0) {
+ replyMac = gwMacAddress;
+ }
+
+ if (replyMac == null) {
+ log.debug("Failed to find MAC address for {}", targetIp);
+ return;
+ }
+
+ Ethernet ethReply = ARP.buildArpReply(
+ targetIp.getIp4Address(),
+ replyMac,
+ ethPacket);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(context.inPacket().receivedFrom().port())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ context.inPacket().receivedFrom().deviceId(),
+ treatment,
+ ByteBuffer.wrap(ethReply.serialize())));
+ }
+
+ private String getArpMode() {
+ Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
+ return getPropertyValue(properties, ARP_MODE);
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+
+ String updatedMac = Tools.get(properties, GATEWAY_MAC);
+ gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
+ log.info("Configured. Gateway MAC is {}", gatewayMac);
+ }
+
+ /**
+ * An internal packet processor which processes ARP request, and results in
+ * packet-out ARP reply.
+ */
+ private class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ }
+
+ Ethernet ethPacket = context.inPacket().parsed();
+ if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
+ return;
+ }
+
+ eventExecutor.execute(() -> processPacketIn(context, ethPacket));
+ }
+ }
+
+ /**
+ * An internal kubernetes node listener which is used for listening kubernetes
+ * node activity. As long as a node is in complete state, we will install
+ * default ARP rule to handle ARP request.
+ */
+ private class InternalNodeEventListener implements K8sNodeListener {
+
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+ }
+
+ @Override
+ public void event(K8sNodeEvent event) {
+ K8sNode k8sNode = event.subject();
+ switch (event.type()) {
+ case K8S_NODE_COMPLETE:
+ eventExecutor.execute(() -> processNodeCompletion(k8sNode));
+ break;
+ case K8S_NODE_INCOMPLETE:
+ eventExecutor.execute(() -> processNodeIncompletion(k8sNode));
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void processNodeCompletion(K8sNode node) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ setDefaultArpRule(node, true);
+ }
+
+ private void processNodeIncompletion(K8sNode node) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ setDefaultArpRule(node, false);
+ }
+
+ private void setDefaultArpRule(K8sNode node, boolean install) {
+
+ if (getArpMode() == null) {
+ return;
+ }
+
+ switch (getArpMode()) {
+ case ARP_PROXY_MODE:
+ setDefaultArpRuleForProxyMode(node, install);
+ break;
+ case ARP_BROADCAST_MODE:
+ // TODO: need to implement broadcast mode
+ log.warn("Not implemented yet.");
+ break;
+ default:
+ log.warn("Invalid ARP mode {}. Please use either " +
+ "broadcast or proxy mode.", getArpMode());
+ break;
+ }
+ }
+
+ private void setDefaultArpRuleForProxyMode(K8sNode node, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .punt()
+ .build();
+
+ k8sFlowRuleService.setRule(
+ appId,
+ node.intgBridge(),
+ selector,
+ treatment,
+ PRIORITY_ARP_CONTROL_RULE,
+ ARP_TABLE,
+ install
+ );
+ }
+ }
+}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
new file mode 100644
index 0000000..53af926
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snetworking.impl;
+
+import com.google.common.base.Strings;
+import org.onlab.packet.Ethernet;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snetworking.api.K8sFlowRuleService;
+import org.onosproject.k8snetworking.api.K8sNetwork;
+import org.onosproject.k8snetworking.api.K8sNetworkEvent;
+import org.onosproject.k8snetworking.api.K8sNetworkListener;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snetworking.api.K8sPort;
+import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
+import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
+import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
+import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Populates switching flow rules on OVS for the basic connectivity among the
+ * container in the same network.
+ */
+@Component(immediate = true)
+public class K8sSwitchingHandler {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String ARP_MODE = "arpMode";
+ private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " +
+ "%s: Failed to get VNI for %s";
+ private static final String STR_NONE = "<none>";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ComponentConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sFlowRuleService k8sFlowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sNetworkService k8sNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected K8sNodeService k8sNodeService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+ private final InternalK8sNetworkListener k8sNetworkListener =
+ new InternalK8sNetworkListener();
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
+ k8sNetworkService.addListener(k8sNetworkListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ k8sNetworkService.removeListener(k8sNetworkListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ /**
+ * Configures the flow rules which are used for L2 packet switching.
+ * Note that these rules will be inserted in switching table (table 5).
+ *
+ * @param port kubernetes port object
+ * @param install install flag, add the rule if true, remove it otherwise
+ */
+ private void setForwardingRulesForTunnel(K8sPort port, boolean install) {
+ // switching rules for the instPorts in the same node
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ // TODO: need to handle IPv6 in near future
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(port.ipAddress().toIpPrefix())
+ .matchTunnelId(getVni(port))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(port.macAddress())
+ .setOutput(port.portNumber())
+ .build();
+
+ k8sFlowRuleService.setRule(
+ appId,
+ port.deviceId(),
+ selector,
+ treatment,
+ PRIORITY_SWITCHING_RULE,
+ FORWARDING_TABLE,
+ install);
+
+ // switching rules for the node in the remote node
+ K8sNode localNode = k8sNodeService.node(port.deviceId());
+ if (localNode == null) {
+ final String error = String.format("Cannot find kubernetes node for %s",
+ port.deviceId());
+ throw new IllegalStateException(error);
+ }
+ k8sNodeService.completeNodes().stream()
+ .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
+ .forEach(remoteNode -> {
+ PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
+ k8sNetworkService, remoteNode);
+ TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+ .extension(buildExtension(
+ deviceService,
+ remoteNode.intgBridge(),
+ localNode.dataIp().getIp4Address()),
+ remoteNode.intgBridge())
+ .setOutput(portNum)
+ .build();
+
+ k8sFlowRuleService.setRule(
+ appId,
+ remoteNode.intgBridge(),
+ selector,
+ treatmentToRemote,
+ PRIORITY_SWITCHING_RULE,
+ FORWARDING_TABLE,
+ install);
+ });
+ }
+
+ private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
+ setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
+ }
+
+ private void setTunnelTagIpFlowRules(K8sPort port, boolean install) {
+ setTunnelTagFlowRules(port, Ethernet.TYPE_IPV4, install);
+ }
+
+ private void setNetworkRulesForTunnel(K8sPort port, boolean install) {
+ setTunnelTagIpFlowRules(port, install);
+ setForwardingRulesForTunnel(port, install);
+
+ if (ARP_BROADCAST_MODE.equals(getArpMode())) {
+ setTunnelTagArpFlowRules(port, install);
+ }
+ }
+
+ /**
+ * Configures the flow rule which is for using VXLAN/GRE/GENEVE to tag the packet
+ * based on the in_port number of a virtual instance.
+ * Note that this rule will be inserted in vTag table.
+ *
+ * @param port kubernetes port object
+ * @param install install flag, add the rule if true, remove it otherwise
+ */
+ private void setTunnelTagFlowRules(K8sPort port, short ethType, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(ethType)
+ .matchInPort(port.portNumber())
+ .build();
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+ .setTunnelId(getVni(port));
+
+
+ if (ethType == Ethernet.TYPE_ARP) {
+ tBuilder.transition(ARP_TABLE);
+ } else if (ethType == Ethernet.TYPE_IPV4) {
+ tBuilder.transition(ACL_EGRESS_TABLE);
+ }
+
+ k8sFlowRuleService.setRule(
+ appId,
+ port.deviceId(),
+ selector,
+ tBuilder.build(),
+ PRIORITY_TUNNEL_TAG_RULE,
+ VTAG_TABLE,
+ install);
+ }
+
+ /**
+ * Obtains the VNI from the given kubernetes port.
+ *
+ * @param port kubernetes port object
+ * @return Virtual Network Identifier (VNI)
+ */
+ private Long getVni(K8sPort port) {
+ K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
+ if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
+ final String error =
+ String.format(ERR_SET_FLOWS_VNI,
+ port, k8sNet == null ? STR_NONE : k8sNet.name());
+ throw new IllegalStateException(error);
+ }
+ return Long.valueOf(k8sNet.segmentId());
+ }
+
+ private void setNetworkRules(K8sPort port, boolean install) {
+ K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
+
+ if (k8sNet == null) {
+ log.warn("Network {} is not found from port {}.", port.networkId(), port.portId());
+ return;
+ }
+
+ switch (k8sNet.type()) {
+ case VXLAN:
+ case GRE:
+ case GENEVE:
+ setNetworkRulesForTunnel(port, install);
+ break;
+ default:
+ log.warn("The given network type {} is not supported.", k8sNet.type().name());
+ break;
+ }
+ }
+
+ private String getArpMode() {
+ Set<ConfigProperty> properties =
+ configService.getProperties(K8sSwitchingArpHandler.class.getName());
+ return getPropertyValue(properties, ARP_MODE);
+ }
+
+ private class InternalK8sNetworkListener implements K8sNetworkListener {
+
+ private boolean isRelevantHelper(K8sNetworkEvent event) {
+ return mastershipService.isLocalMaster(event.port().deviceId());
+ }
+
+ @Override
+ public void event(K8sNetworkEvent event) {
+ switch (event.type()) {
+ case K8S_PORT_ACTIVATED:
+ eventExecutor.execute(() -> processInstanceDetection(event));
+ break;
+ case K8S_PORT_REMOVED:
+ eventExecutor.execute(() -> processInstanceRemoval(event));
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void processInstanceDetection(K8sNetworkEvent event) {
+ if (!isRelevantHelper(event)) {
+ return;
+ }
+
+ setNetworkRules(event.port(), true);
+ }
+
+ private void processInstanceRemoval(K8sNetworkEvent event) {
+ if (!isRelevantHelper(event)) {
+ return;
+ }
+
+ setNetworkRules(event.port(), false);
+ }
+ }
+}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHostProvider.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHostProvider.java
index f7f78c9..1dec9d1 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHostProvider.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHostProvider.java
@@ -22,7 +22,7 @@
import org.onlab.util.Tools;
import org.onosproject.core.CoreService;
import org.onosproject.k8snetworking.api.K8sNetwork;
-import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snetworking.api.K8sNetworkAdminService;
import org.onosproject.k8snetworking.api.K8sPort;
import org.onosproject.k8snode.api.K8sNode;
import org.onosproject.k8snode.api.K8sNodeEvent;
@@ -79,8 +79,8 @@
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String ERR_ADD_HOST = "Failed to add host: ";
- private static final String SONA_HOST_SCHEME = "sona";
- private static final int PORT_PREFIX_LENGTH = 3;
+ private static final String SONA_HOST_SCHEME = "sona-k8s";
+ private static final int PORT_PREFIX_LENGTH = 4;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@@ -98,7 +98,7 @@
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected K8sNetworkService k8sNetworkService;
+ protected K8sNetworkAdminService k8sNetworkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected K8sNodeService k8sNodeService;
@@ -174,6 +174,11 @@
long createTime = System.currentTimeMillis();
+ // update k8s port number by referring to ONOS port number
+
+ k8sNetworkService.updatePort(k8sPort.updatePortNumber(port.number())
+ .updateState(K8sPort.State.ACTIVE));
+
// we check whether the host already attached to same locations
Host host = hostService.getHost(hostId);
@@ -291,6 +296,10 @@
return;
}
+ log.debug("K8s port {} is updated at {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+
if (!event.port().isEnabled()) {
processPortRemoval(event);
} else if (event.port().isEnabled()) {
@@ -303,7 +312,11 @@
return;
}
- processPortAddition(event);
+ log.debug("K8s port {} is detected from {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+
+ processPortAdded(event.port());
}
private void processPortRemoval(DeviceEvent event) {
@@ -311,7 +324,11 @@
return;
}
- processPortRemoval(event);
+ log.debug("K8s port {} is removed from {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+
+ processPortRemoved(event.port());
}
}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/OsgiPropertyConstants.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/OsgiPropertyConstants.java
new file mode 100644
index 0000000..734b8e5
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/OsgiPropertyConstants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snetworking.impl;
+
+/**
+ * Name/Value constants for properties.
+ */
+public final class OsgiPropertyConstants {
+
+ private OsgiPropertyConstants() {
+ }
+
+ static final String GATEWAY_MAC = "gatewayMac";
+ static final String GATEWAY_MAC_DEFAULT = "fe:00:00:00:00:02";
+
+ static final String ARP_MODE = "arpMode";
+ static final String ARP_MODE_DEFAULT = "proxy";
+
+ static final String DHCP_SERVER_MAC = "dhcpServerMac";
+ static final String DHCP_SERVER_MAC_DEFAULT = "fe:00:00:00:00:02";
+}
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
index ad8c842..f6fd629 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
@@ -19,10 +19,17 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.k8snetworking.api.K8sNetwork;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.net.PortNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Optional;
+import java.util.Set;
import static org.onosproject.k8snetworking.api.Constants.PORT_NAME_PREFIX_CONTAINER;
@@ -43,7 +50,62 @@
* @return true if the port is associated with container; false otherwise
*/
public static boolean isContainer(String portName) {
- return PORT_NAME_PREFIX_CONTAINER.equals(portName);
+ return portName != null && portName.contains(PORT_NAME_PREFIX_CONTAINER);
+ }
+
+ /**
+ * Returns the tunnel port number with specified net ID and kubernetes node.
+ *
+ * @param netId network ID
+ * @param netService network service
+ * @param node kubernetes node
+ * @return tunnel port number
+ */
+ public static PortNumber tunnelPortNumByNetId(String netId,
+ K8sNetworkService netService,
+ K8sNode node) {
+ K8sNetwork.Type netType = netService.network(netId).type();
+
+ if (netType == null) {
+ return null;
+ }
+
+ return tunnelPortNumByNetType(netType, node);
+ }
+
+ /**
+ * Returns the tunnel port number with specified net type and kubernetes node.
+ *
+ * @param netType network type
+ * @param node kubernetes node
+ * @return tunnel port number
+ */
+ public static PortNumber tunnelPortNumByNetType(K8sNetwork.Type netType,
+ K8sNode node) {
+ switch (netType) {
+ case VXLAN:
+ return node.vxlanPortNum();
+ case GRE:
+ return node.grePortNum();
+ case GENEVE:
+ return node.genevePortNum();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Obtains the property value with specified property key name.
+ *
+ * @param properties a collection of properties
+ * @param name key name
+ * @return mapping value
+ */
+ public static String getPropertyValue(Set<ConfigProperty> properties,
+ String name) {
+ Optional<ConfigProperty> property =
+ properties.stream().filter(p -> p.name().equals(name)).findFirst();
+ return property.map(ConfigProperty::value).orElse(null);
}
/**
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java
new file mode 100644
index 0000000..d5366ae
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snetworking.util;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.slf4j.Logger;
+
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides common methods to help populating flow rules for SONA applications.
+ */
+public final class RulePopulatorUtil {
+
+ private static final Logger log = getLogger(RulePopulatorUtil.class);
+
+ private static final String TUNNEL_DST = "tunnelDst";
+
+ private RulePopulatorUtil() {
+ }
+
+ /**
+ * Returns tunnel destination extension treatment object.
+ *
+ * @param deviceService driver service
+ * @param deviceId device id to apply this treatment
+ * @param remoteIp tunnel destination ip address
+ * @return extension treatment
+ */
+ public static ExtensionTreatment buildExtension(DeviceService deviceService,
+ DeviceId deviceId,
+ Ip4Address remoteIp) {
+ Device device = deviceService.getDevice(deviceId);
+ if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
+ log.error("The extension treatment is not supported");
+ return null;
+ }
+
+ if (device == null) {
+ return null;
+ }
+
+ ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+ ExtensionTreatment treatment =
+ resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+ try {
+ treatment.setPropertyValue(TUNNEL_DST, remoteIp);
+ return treatment;
+ } catch (ExtensionPropertyException e) {
+ log.warn("Failed to get tunnelDst extension treatment for {} " +
+ "because of {}", deviceId, e);
+ return null;
+ }
+ }
+}
diff --git a/apps/k8s-node/BUILD b/apps/k8s-node/BUILD
index 83aaf44..b17a604 100644
--- a/apps/k8s-node/BUILD
+++ b/apps/k8s-node/BUILD
@@ -7,6 +7,10 @@
category = "Integration",
description = "SONA Kubernetes Node Application.",
included_bundles = BUNDLES,
+ required_apps = [
+ "org.onosproject.ovsdb-base",
+ "org.onosproject.drivers.ovsdb",
+ ],
title = "Kubernetes Node Application",
url = "https://wiki.onosproject.org/display/ONOS/SONA%3A+DC+Network+Virtualization",
)
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
index 60c02b4..5f22f70 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
@@ -28,6 +28,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.k8snode.api.Constants.GENEVE_TUNNEL;
import static org.onosproject.k8snode.api.Constants.GRE_TUNNEL;
+import static org.onosproject.k8snode.api.Constants.INTEGRATION_BRIDGE;
import static org.onosproject.k8snode.api.Constants.VXLAN_TUNNEL;
import static org.onosproject.net.AnnotationKeys.PORT_NAME;
@@ -130,6 +131,16 @@
}
@Override
+ public PortNumber intBridgePortNum() {
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), INTEGRATION_BRIDGE))
+ .findAny().orElse(null);
+ return port != null ? port.number() : null;
+ }
+
+ @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
index 13b1f47..4abc2cb 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
@@ -118,6 +118,13 @@
PortNumber genevePortNum();
/**
+ * Returns the host port number.
+ *
+ * @return host port number; null if the host port does not exist
+ */
+ PortNumber intBridgePortNum();
+
+ /**
* Builder of new node entity.
*/
interface Builder {