Initial support VM and container communication via POD and service IP

Change-Id: Ic87beee6ed122ec5551370c2b6a2789edf8fba5b
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
new file mode 100644
index 0000000..2f5c1d1
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2020-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.openstacknetworking.impl;
+
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+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.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
+import org.onosproject.openstacknetworking.api.OpenstackGroupRuleService;
+import org.onosproject.openstacknetworking.api.OpenstackK8sIntegrationService;
+import org.onosproject.openstacknetworking.api.OpenstackNetwork;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+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 static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
+import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CNI_PT_IP_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.STAT_FLAT_OUTBOUND_TABLE;
+import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.shiftIpDomain;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of openstack kubernetes integration service.
+ */
+
+@Component(
+    immediate = true,
+    service = { OpenstackK8sIntegrationService.class }
+)
+public class OpenstackK8sIntegrationManager implements OpenstackK8sIntegrationService {
+
+    protected final Logger log = getLogger(getClass());
+
+    public static final String SHIFTED_IP_PREFIX = "172.10";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackFlowRuleService osFlowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackGroupRuleService osGroupRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected InstancePortService instancePortService;
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public void installCniPtNodeRules(IpAddress k8sNodeIp,
+                                      IpPrefix podCidr, IpPrefix serviceCidr,
+                                      IpAddress podGatewayIp, String osK8sIntPortName,
+                                      MacAddress k8sIntOsPortMac) {
+        setNodeToPodIpRules(k8sNodeIp, podCidr, serviceCidr,
+                podGatewayIp, osK8sIntPortName, k8sIntOsPortMac, true);
+        setPodToNodeIpRules(k8sNodeIp, podGatewayIp, osK8sIntPortName, true);
+    }
+
+    @Override
+    public void uninstallCniPtNodeRules(IpAddress k8sNodeIp,
+                                        IpPrefix podCidr, IpPrefix serviceCidr,
+                                        IpAddress podGatewayIp, String osK8sIntPortName,
+                                        MacAddress k8sIntOsPortMac) {
+        setNodeToPodIpRules(k8sNodeIp, podCidr, serviceCidr,
+                podGatewayIp, osK8sIntPortName, k8sIntOsPortMac, false);
+        setPodToNodeIpRules(k8sNodeIp, podGatewayIp, osK8sIntPortName, false);
+    }
+
+    private void setNodeToPodIpRules(IpAddress k8sNodeIp,
+                                     IpPrefix podCidr, IpPrefix serviceCidr,
+                                     IpAddress gatewayIp, String osK8sIntPortName,
+                                     MacAddress k8sIntOsPortMac, boolean install) {
+
+        InstancePort instPort = instancePortService.instancePorts().stream().filter(p -> {
+            OpenstackNetwork.Type netType = osNetworkService.networkType(p.networkId());
+            return netType == FLAT && p.ipAddress().equals(k8sNodeIp);
+        }).findAny().orElse(null);
+
+        if (instPort == null) {
+            return;
+        }
+
+        DeviceId deviceId = instPort.deviceId();
+        OpenstackNode osNode = osNodeService.node(deviceId);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sIntPortNum = osNode.portNumByName(osK8sIntPortName);
+
+        if (osK8sIntPortNum == null) {
+            return;
+        }
+
+        TrafficSelector originalPodSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(podCidr)
+                .build();
+
+        TrafficSelector transformedPodSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(IpPrefix.valueOf(shiftIpDomain(podCidr.toString(), SHIFTED_IP_PREFIX)))
+                .build();
+
+        TrafficSelector serviceSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(serviceCidr)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setIpSrc(gatewayIp)
+                .setEthSrc(k8sIntOsPortMac)
+                .setOutput(osK8sIntPortNum)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                originalPodSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                transformedPodSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                serviceSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+    }
+
+    private void setPodToNodeIpRules(IpAddress k8sNodeIp, IpAddress gatewayIp,
+                                     String osK8sIntPortName, boolean install) {
+        InstancePort instPort = instancePortService.instancePorts().stream().filter(p -> {
+            OpenstackNetwork.Type netType = osNetworkService.networkType(p.networkId());
+            return netType == FLAT && p.ipAddress().equals(k8sNodeIp);
+        }).findAny().orElse(null);
+
+        if (instPort == null) {
+            return;
+        }
+
+        DeviceId deviceId = instPort.deviceId();
+        OpenstackNode osNode = osNodeService.node(deviceId);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sIntPortNum = osNode.portNumByName(osK8sIntPortName);
+
+        if (osK8sIntPortNum == null) {
+            return;
+        }
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(gatewayIp, 32))
+                .matchInPort(osK8sIntPortNum)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setIpDst(k8sNodeIp)
+                .setEthDst(instPort.macAddress())
+                .transition(STAT_FLAT_OUTBOUND_TABLE)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                DHCP_TABLE,
+                install
+        );
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index f061510..5d7c347 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -718,13 +718,13 @@
                         osNetworkService, remoteNode);
 
                 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                .extension(buildExtension(
-                        deviceService,
-                        remoteNode.intgBridge(),
-                        localNode.dataIp().getIp4Address()),
-                        remoteNode.intgBridge())
-                .setOutput(portNum)
-                .build();
+                        .extension(buildExtension(
+                                deviceService,
+                                remoteNode.intgBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                remoteNode.intgBridge())
+                        .setOutput(portNum)
+                        .build();
 
                 osFlowRuleService.setRule(
                         appId,
@@ -812,9 +812,9 @@
     }
 
     private void processFlowTableRules(OpenstackNode osNode,
-                                        String segId, String netId,
-                                        boolean isTunnel,
-                                        boolean install) {
+                                       String segId, String netId,
+                                       boolean isTunnel,
+                                       boolean install) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .matchArpOp(ARP.OP_REQUEST);
@@ -1096,8 +1096,8 @@
 
             netIds.stream()
                     .filter(nid -> osNetworkService.networkType(nid) == VXLAN ||
-                                    osNetworkService.networkType(nid) == GRE ||
-                                    osNetworkService.networkType(nid) == GENEVE)
+                            osNetworkService.networkType(nid) == GRE ||
+                            osNetworkService.networkType(nid) == GENEVE)
                     .forEach(nid -> {
                         String segId = osNetworkService.segmentId(nid);
                         setBaseVnetArpRuleForBroadcastMode(osNode, segId, nid, true, install);
@@ -1119,7 +1119,7 @@
                         .forEach(p -> {
                             setArpRequestRule(p, install);
                             setArpReplyRule(p, install);
-                });
+                        });
             } else {
                 // we do nothing for proxy mode
             }
@@ -1190,4 +1190,4 @@
             setArpReplyRule(event.subject(), false);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 87b8d50..6df6aac 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -1518,6 +1518,34 @@
         }
     }
 
+    /**
+     * Returns a shifted IP address.
+     *
+     * @param ipAddress     IP address to be shifted
+     * @param shiftPrefix   A IP prefix used in shifted IP address
+     * @return shifted Ip address
+     */
+    public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
+        String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
+        return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
+    }
+
+    /**
+     * Returns an unshifted IP address.
+     *
+     * @param ipAddress     IP address to be unshifted
+     * @param ipPrefix      IP prefix which to be used for unshifting
+     * @param cidr          a POD network CIDR
+     * @return unshifted IP address
+     */
+    public static String unshiftIpDomain(String ipAddress,
+                                         String ipPrefix,
+                                         String cidr) {
+
+        String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
+        return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
+    }
+
     private static String getDhcpOptionValue(NeutronPort port, String optionNameStr) {
         ObjectNode node = modelEntityToJson(port, NeutronPort.class);
 
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
new file mode 100644
index 0000000..c38984b
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020-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.openstacknetworking.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.openstacknetworking.api.OpenstackK8sIntegrationService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * REST interface for integrating openstack and kubernetes.
+ */
+@Path("integration")
+public class OpenstackK8sIntegrationWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String K8S_NODE_IP = "k8sNodeIp";
+    private static final String OS_K8S_INT_PORT_NAME = "osK8sIntPortName";
+    private static final String POD_CIDR = "podCidr";
+    private static final String SERVICE_CIDR = "serviceCidr";
+    private static final String POD_GW_IP = "podGwIp";
+    private static final String K8S_INT_OS_PORT_MAC = "k8sIntOsPortMac";
+
+    private final OpenstackK8sIntegrationService intService =
+            get(OpenstackK8sIntegrationService.class);
+
+    /**
+     * Installs CNI pass-through related flow rules for each kubernetes nodes.
+     *
+     * @param input     JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("node/pt-install")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response installCniPtNodeRules(InputStream input) throws IOException {
+        log.trace("Install K8S CNI pass-through node rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        IpPrefix podCidr = IpPrefix.valueOf(json.get(POD_CIDR).asText());
+        IpPrefix serviceCidr = IpPrefix.valueOf(json.get(SERVICE_CIDR).asText());
+        IpAddress podGwIp = IpAddress.valueOf(json.get(POD_GW_IP).asText());
+        String osK8sIntPortName = json.get(OS_K8S_INT_PORT_NAME).asText();
+        MacAddress k8sIntOsPortMac = MacAddress.valueOf(json.get(K8S_INT_OS_PORT_MAC).asText());
+
+        intService.installCniPtNodeRules(k8sNodeIp, podCidr, serviceCidr,
+                podGwIp, osK8sIntPortName, k8sIntOsPortMac);
+
+        return Response.ok().build();
+    }
+
+    /**
+     * Installs CNI pass-through related flow rules for each kubernetes nodes.
+     *
+     * @param input     JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("node/pt-uninstall")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response uninstallCniPtNodeRules(InputStream input) throws IOException {
+        log.trace("Uninstall K8S CNI pass-through node rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        IpPrefix podCidr = IpPrefix.valueOf(json.get(POD_CIDR).asText());
+        IpPrefix serviceCidr = IpPrefix.valueOf(json.get(SERVICE_CIDR).asText());
+        IpAddress podGwIp = IpAddress.valueOf(json.get(POD_GW_IP).asText());
+        String osK8sIntPortName = json.get(OS_K8S_INT_PORT_NAME).asText();
+        MacAddress k8sIntOsPortMac = MacAddress.valueOf(json.get(K8S_INT_OS_PORT_MAC).asText());
+
+        intService.uninstallCniPtNodeRules(k8sNodeIp, podCidr, serviceCidr,
+                podGwIp, osK8sIntPortName, k8sIntOsPortMac);
+
+        return Response.ok().build();
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
index f215efe..f6b3e65 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
@@ -33,7 +33,8 @@
                 OpenstackSecurityGroupRuleWebResource.class,
                 OpenstackSecurityGroupWebResource.class,
                 OpenstackSubnetWebResource.class,
-                OpenstackManagementWebResource.class
+                OpenstackManagementWebResource.class,
+                OpenstackK8sIntegrationWebResource.class
         );
     }
 }