Detangling incubator: virtual nets, tunnels, resource labels, oh my

- virtual networking moved to /apps/virtual; with CLI & REST API
- tunnels and labels moved to /apps/tunnel; with CLI & REST API; UI disabled for now
- protobuf/models moved to /core/protobuf/models
- defunct grpc/rpc registry stuff left under /graveyard
- compile dependencies on /incubator moved to respective modules for compilation
- run-time dependencies will need to be re-tested for dependent apps

- /graveyard will be removed in not-too-distant future

Change-Id: I0a0b995c635487edcf95a352f50dd162186b0b39
diff --git a/apps/dpistats/app/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java b/apps/dpistats/app/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
new file mode 100644
index 0000000..f1fb721
--- /dev/null
+++ b/apps/dpistats/app/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2018-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.incubator.net.dpi.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.dpi.DpiStatInfo;
+import org.onosproject.incubator.net.dpi.DpiStatistics;
+import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService;
+import org.onosproject.incubator.net.dpi.FlowStatInfo;
+import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
+import org.onosproject.incubator.net.dpi.TrafficStatInfo;
+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.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static java.lang.Thread.sleep;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * DPI Statistics Manager.
+ */
+@Component(immediate = true, service = DpiStatisticsManagerService.class)
+public class DpiStatisticsManager implements DpiStatisticsManagerService {
+
+    private ServerSocket serverSocket;
+    private static int port = 11990; // socket server listening port
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    private ApplicationId appId;
+
+    private final ExecutorService dpiListenerThread =
+            Executors.newSingleThreadExecutor(groupedThreads("onos/apps/dpi", "dpi-listener"));
+
+    DpiStatisticsListener dpiStatisticsListener = null;
+
+    // 31*2(month)*24(hour)*3600(second)/5(second)
+    private static final int MAX_DPI_STATISTICS_ENTRY = 1071360;
+
+    private SortedMap<String, DpiStatistics> dpiStatisticsMap =
+            new TreeMap<>(new MapComparator());
+
+    private long convertTimeToLong(String timeString) {
+        long timeLong = 0;
+
+        try {
+            // Time format: yyyy-MM-dd HH:mm:ss, Time Zone: GMT
+            SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
+            df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
+
+             timeLong = df.parse(timeString).getTime();
+        } catch (ParseException e) {
+            log.error("Time parse error! Exception={}", e.toString());
+        }
+
+        return timeLong;
+    }
+
+    private static final String DATE_FMT = "yyyy-MM-dd HH:mm:ss";
+    private static final String TIME_ZONE = "GMT";
+
+    public static final int MAX_DPI_STATISTICS_REQUEST = 100;
+    public static final int MAX_DPI_STATISTICS_TOPN = 100;
+
+    @Activate
+    public void activate(ComponentContext context) {
+        appId = coreService.registerApplication("org.onosproject.dpi");
+
+//        registerCodec(DpiStatistics.class, new DpiStatisticsCodec());
+//        registerCodec(DpiStatInfo.class, new DpiStatInfoCodec());
+//        registerCodec(TrafficStatInfo.class, new TrafficStatInfoCodec());
+//        registerCodec(ProtocolStatInfo.class, new ProtocolStatInfoCodec());
+//        registerCodec(FlowStatInfo.class, new FlowStatInfoCodec());
+
+        dpiStatisticsListener = new DpiStatisticsListener();
+        dpiListenerThread.execute(dpiStatisticsListener);
+
+        log.info("Started", appId.id());
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Deactivated...");
+        dpiListenerThread.shutdownNow();
+        log.info("Stopped");
+    }
+
+    @Override
+    public DpiStatistics getDpiStatisticsLatest() {
+        if (dpiStatisticsMap.size() > 0) {
+            return dpiStatisticsMap.get(dpiStatisticsMap.firstKey());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public DpiStatistics getDpiStatisticsLatest(int topnProtocols, int topnFlows) {
+        DpiStatistics ds, topnDs;
+
+        ds = getDpiStatisticsLatest();
+        topnDs = processTopn(ds, topnProtocols, topnFlows);
+
+        return topnDs;
+    }
+
+    @Override
+    public List<DpiStatistics> getDpiStatistics(int lastN) {
+        List<DpiStatistics> dsList = new ArrayList<>();
+        DpiStatistics ds;
+
+        if (lastN > MAX_DPI_STATISTICS_REQUEST) {
+            lastN = MAX_DPI_STATISTICS_REQUEST;
+        }
+
+        SortedMap tempMap = new TreeMap(new MapComparator());
+        tempMap.putAll(dpiStatisticsMap);
+
+        for (int i = 0; i < lastN && i < tempMap.size(); i++) {
+            ds = (DpiStatistics) tempMap.get(tempMap.firstKey());
+            dsList.add(i, new DpiStatistics(ds.receivedTime(), ds.dpiStatInfo()));
+
+            tempMap.remove(tempMap.firstKey());
+        }
+
+        return dsList;
+    }
+
+    @Override
+    public List<DpiStatistics> getDpiStatistics(int lastN, int topnProtocols, int topnFlows) {
+        List<DpiStatistics> dsList;
+        List<DpiStatistics> topnDsList = new ArrayList<>();
+        DpiStatistics ds, topnDs;
+
+        dsList = getDpiStatistics(lastN);
+        for (int i = 0; i < dsList.size(); i++) {
+            ds = dsList.get(i);
+            topnDs = processTopn(ds, topnProtocols, topnFlows);
+            topnDsList.add(i, topnDs);
+        }
+
+        return topnDsList;
+    }
+
+    @Override
+    public DpiStatistics getDpiStatistics(String receivedTime) {
+        DpiStatistics ds;
+
+        if (receivedTime == null) {
+            return null;
+        }
+
+        if (!dpiStatisticsMap.containsKey(receivedTime)) {
+            return null;
+        }
+
+        ds = dpiStatisticsMap.get(receivedTime);
+
+        return ds;
+    }
+
+    @Override
+    public DpiStatistics getDpiStatistics(String receivedTime, int topnProtocols, int topnFlows) {
+        DpiStatistics ds, topnDs;
+
+        ds = getDpiStatistics(receivedTime);
+
+        topnDs = processTopn(ds, topnProtocols, topnFlows);
+
+        return topnDs;
+    }
+
+    @Override
+    public DpiStatistics addDpiStatistics(DpiStatistics ds) {
+        if (ds == null) {
+            return ds;
+        }
+
+        // check the time. The firstKey is lastTime because of descending sorted order
+        if (dpiStatisticsMap.size() > 0) {
+            String lastTime = dpiStatisticsMap.get(dpiStatisticsMap.firstKey()).receivedTime();
+            String inputTime = ds.receivedTime();
+
+            long lastTimeLong = convertTimeToLong(lastTime);
+            long inputTimeLong = convertTimeToLong(inputTime);
+
+            if (lastTimeLong >= inputTimeLong) {
+                return null;
+            }
+        }
+
+        if (dpiStatisticsMap.size() >= MAX_DPI_STATISTICS_ENTRY) {
+            // remove the last (oldest) entry
+            dpiStatisticsMap.remove(dpiStatisticsMap.lastKey());
+        }
+
+        if (dpiStatisticsMap.containsKey(ds.receivedTime())) {
+            log.warn("addDpiStatistics(), {} dpiStatistics is already existing!",
+                      ds.receivedTime());
+            return null;
+        }
+
+        dpiStatisticsMap.put(ds.receivedTime(), ds);
+        log.debug("addDpiStatistics: dpiResultJson data[time={}] is added " +
+                          "into DpiStatisticsMap size={}.",
+                  ds.receivedTime(), dpiStatisticsMap.size());
+
+        return ds;
+    }
+
+    private class MapComparator implements Comparator<String> {
+        @Override
+        public int compare(String rt1, String rt2) {
+            long rt1Long = convertTimeToLong(rt1);
+            long rt2Long = convertTimeToLong(rt2);
+
+            // Descending order
+            if (rt1Long > rt2Long) {
+                return -1;
+            } else if (rt1Long < rt2Long) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private class ProtocolComparator implements Comparator<ProtocolStatInfo> {
+        @Override
+        public int compare(ProtocolStatInfo p1, ProtocolStatInfo p2) {
+            //Descending order
+            if (p1.bytes() > p2.bytes()) {
+                return -1;
+            } else if (p1.bytes() < p2.bytes()) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    private class FlowComparator implements Comparator<FlowStatInfo> {
+        @Override
+        public int compare(FlowStatInfo f1, FlowStatInfo f2) {
+            // Descending order
+            if (f1.bytes() > f2.bytes()) {
+                return -1;
+            } else if (f1.bytes() < f2.bytes()) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+    private DpiStatistics processTopn(DpiStatistics ds, int topnProtocols, int topnFlows) {
+        if (ds == null) {
+            return null;
+        }
+
+        if (topnProtocols <= 0) {
+            // displays all entries
+            topnProtocols = 0;
+        } else if (topnProtocols > MAX_DPI_STATISTICS_TOPN) {
+            topnProtocols = MAX_DPI_STATISTICS_TOPN;
+        }
+
+        if (topnFlows <= 0) {
+            // displays all entries
+            topnFlows = 0;
+        } else if (topnFlows > MAX_DPI_STATISTICS_TOPN) {
+            topnFlows = MAX_DPI_STATISTICS_TOPN;
+        }
+
+        if (topnProtocols == 0 && topnFlows == 0) {
+            return ds;
+        }
+
+        TrafficStatInfo tsi = ds.dpiStatInfo().trafficStatistics();
+        List<ProtocolStatInfo> psiList;
+        List<FlowStatInfo> kfList;
+        List<FlowStatInfo> ufList;
+
+        List<ProtocolStatInfo> pList = ds.dpiStatInfo().detectedProtos();
+        Collections.sort(pList, new ProtocolComparator());
+        if (topnProtocols > 0 && topnProtocols < pList.size()) {
+            psiList = pList.subList(0, topnProtocols);
+        } else {
+            psiList = pList;
+        }
+
+
+        List<FlowStatInfo> fList = ds.dpiStatInfo().knownFlows();
+        Collections.sort(fList, new FlowComparator());
+        if (topnFlows > 0 && topnFlows < fList.size()) {
+            kfList = fList.subList(0, topnFlows);
+        } else {
+            kfList = fList;
+        }
+
+        fList = ds.dpiStatInfo().unknownFlows();
+        Collections.sort(fList, new FlowComparator());
+        if (topnFlows > 0 && topnFlows < fList.size()) {
+            ufList = fList.subList(0, topnFlows);
+        } else {
+            ufList = fList;
+        }
+
+        DpiStatInfo dsi = new DpiStatInfo();
+        dsi.setTrafficStatistics(tsi);
+        dsi.setDetectedProtos(psiList);
+        dsi.setKnownFlows(kfList);
+        dsi.setUnknownFlows(ufList);
+
+        DpiStatistics retDs = new DpiStatistics(ds.receivedTime(), dsi);
+        return retDs;
+    }
+
+    /**
+     * Receiving DPI Statistics result thread.
+     */
+    private class DpiStatisticsListener implements Runnable {
+        Socket clientSocket = null;
+        BufferedReader in = null;
+        PrintWriter out = null;
+
+        String resultJsonString = null;
+
+        static final int MAX_SLEEP_COUNT = 10;
+        int sleepCount = 0;
+
+        @Override
+        public void run() {
+            log.info("DpiStatisticsListener: Receiving thread started...");
+            receiveDpiResult();
+        }
+
+        private void receiveDpiResult() {
+            try {
+                serverSocket = new ServerSocket(port);
+            } catch (Exception e) {
+                log.error("DpiStatisticsListener: ServerSocket listening error from port={} in localhost, exception={}",
+                          port, e.toString());
+                return;
+            }
+
+            try {
+                while (!Thread.currentThread().isInterrupted()) {
+                    if (clientSocket == null) {
+                        log.info("DpiStatisticsListener: Waiting for accepting from dpi client...");
+                        clientSocket = serverSocket.accept();
+                        log.info("DpiStatisticsListener: Accepted from dpi client={}",
+                                 clientSocket.getRemoteSocketAddress().toString());
+
+                        in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+                        out = new PrintWriter(clientSocket.getOutputStream(), true); // For disconnecting check!
+
+                        resultJsonString = null;
+                    }
+
+                    sleepCount = 0;
+                    while (!in.ready()) {
+                        sleep(1000); // sleep one second.
+                        if (out.checkError() || ++sleepCount >= MAX_SLEEP_COUNT) {
+                            log.debug("DpiStatisticsListener: server and socket connect is lost...");
+                            in.close();
+                            in = null;
+                            out.close();
+                            out = null;
+                            clientSocket.close();
+                            clientSocket = null;
+
+                            break;
+                        }
+                    }
+
+                    if (in != null) {
+                        resultJsonString = in.readLine();
+
+                        // process the result
+                        log.trace("DpiStatisticsListener: resultJsonString={}", resultJsonString);
+                        processResultJson(resultJsonString);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("DpiStatisticsListener: Exception = {}", e.toString());
+                return;
+            } finally {
+                try {
+                    if (serverSocket != null) {
+                        if (clientSocket != null) {
+                            if (in != null) {
+                                in.close();
+                            }
+                            if (out != null) {
+                                out.close();
+                            }
+                            clientSocket.close();
+                            //log.debug("DpiResultListener: stop(): Socket close() is done...");
+                        }
+                        serverSocket.close();
+                        //log.debug("DpiResultListener: stop(): Server close() is done...");
+                    }
+                } catch (Exception e) {
+                    log.error("DpiStatisticsListener: stop(): Server Socket closing error, exception={}",
+                            e.toString());
+                }
+            }
+        }
+
+        private void processResultJson(String resultJsonString) {
+            Date tr = new Date(System.currentTimeMillis());
+            SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
+            df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
+
+            String curReceivedTime = new String(df.format(tr));
+            String curResultJson = new String(resultJsonString);
+
+            DpiStatInfo dpiStatInfo;
+            ObjectMapper mapper = new ObjectMapper();
+            try {
+                dpiStatInfo = mapper.readValue(curResultJson, DpiStatInfo.class);
+            } catch (IOException e) {
+                log.error("DpiStatisticsListener: ObjectMapper Exception = {}", e.toString());
+                return;
+            }
+
+            DpiStatistics dpiStatistics = new DpiStatistics(curReceivedTime, dpiStatInfo);
+
+            addDpiStatistics(dpiStatistics);
+        }
+    }
+}