[ONOS-2818] add implementation of VtnRscService api

Change-Id: I0d5b87813cf0edb1fd5ebefe1eedf8b50fda7a5e
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java
new file mode 100644
index 0000000..ec9ca3e
--- /dev/null
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/VtnRscManager.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.vtnrsc.service.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.LogicalClockService;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.FloatingIp;
+import org.onosproject.vtnrsc.Router;
+import org.onosproject.vtnrsc.RouterInterface;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.event.VtnRscEvent;
+import org.onosproject.vtnrsc.event.VtnRscEventFeedback;
+import org.onosproject.vtnrsc.event.VtnRscListener;
+import org.onosproject.vtnrsc.floatingip.FloatingIpEvent;
+import org.onosproject.vtnrsc.floatingip.FloatingIpListener;
+import org.onosproject.vtnrsc.floatingip.FloatingIpService;
+import org.onosproject.vtnrsc.router.RouterEvent;
+import org.onosproject.vtnrsc.router.RouterListener;
+import org.onosproject.vtnrsc.router.RouterService;
+import org.onosproject.vtnrsc.routerinterface.RouterInterfaceEvent;
+import org.onosproject.vtnrsc.routerinterface.RouterInterfaceListener;
+import org.onosproject.vtnrsc.routerinterface.RouterInterfaceService;
+import org.onosproject.vtnrsc.service.VtnRscService;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Provides implementation of the VtnRsc service.
+ */
+@Component(immediate = true)
+@Service
+public class VtnRscManager implements VtnRscService {
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LogicalClockService clockService;
+
+    private final Logger log = getLogger(getClass());
+    private final Set<VtnRscListener> listeners = Sets.newCopyOnWriteArraySet();
+    private HostListener hostListener = new InnerHostListener();
+    private FloatingIpListener floatingIpListener = new InnerFloatingIpListener();
+    private RouterListener routerListener = new InnerRouterListener();
+    private RouterInterfaceListener routerInterfaceListener = new InnerRouterInterfaceListener();
+
+    private EventuallyConsistentMap<TenantId, SegmentationId> l3vniMap;
+    private EventuallyConsistentMap<TenantId, Set<DeviceId>> classifierOvsMap;
+    private EventuallyConsistentMap<TenantId, Set<DeviceId>> sffOvsMap;
+
+    private static final String IFACEID = "ifaceid";
+    private static final String RUNNELOPTOPOIC = "tunnel-ops-ids";
+    private static final String LISTENER_NOT_NULL = "listener cannot be null";
+    private static final String EVENT_NOT_NULL = "event cannot be null";
+    private static final String TENANTID_NOT_NULL = "tenantId cannot be null";
+    private static final String DEVICEID_NOT_NULL = "deviceId cannot be null";
+    private static final String OVSMAP_NOT_NULL = "ovsMap cannot be null";
+    private static final String L3VNIMAP = "l3vniMap";
+    private static final String CLASSIFIEROVSMAP = "classifierOvsMap";
+    private static final String SFFOVSMAP = "sffOvsMap";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RouterService routerService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FloatingIpService floatingIpService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RouterInterfaceService routerInterfaceService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualPortService virtualPortService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected SubnetService subnetService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TenantNetworkService tenantNetworkService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Activate
+    public void activate() {
+        hostService.addListener(hostListener);
+        floatingIpService.addListener(floatingIpListener);
+        routerService.addListener(routerListener);
+        routerInterfaceService.addListener(routerInterfaceListener);
+
+        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+                .register(KryoNamespaces.API)
+                .register(TenantId.class, DeviceId.class);
+        l3vniMap = storageService
+                .<TenantId, SegmentationId>eventuallyConsistentMapBuilder()
+                .withName(L3VNIMAP).withSerializer(serializer)
+                .withTimestampProvider((k, v) -> clockService.getTimestamp())
+                .build();
+
+        classifierOvsMap = storageService
+                .<TenantId, Set<DeviceId>>eventuallyConsistentMapBuilder()
+                .withName(CLASSIFIEROVSMAP).withSerializer(serializer)
+                .withTimestampProvider((k, v) -> clockService.getTimestamp())
+                .build();
+
+        sffOvsMap = storageService
+                .<TenantId, Set<DeviceId>>eventuallyConsistentMapBuilder()
+                .withName(SFFOVSMAP).withSerializer(serializer)
+                .withTimestampProvider((k, v) -> clockService.getTimestamp())
+                .build();
+    }
+
+    @Deactivate
+    public void deactivate() {
+        hostService.removeListener(hostListener);
+        floatingIpService.removeListener(floatingIpListener);
+        routerService.removeListener(routerListener);
+        routerInterfaceService.removeListener(routerInterfaceListener);
+        l3vniMap.destroy();
+        classifierOvsMap.destroy();
+        sffOvsMap.destroy();
+        listeners.clear();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void addListener(VtnRscListener listener) {
+        checkNotNull(listener, LISTENER_NOT_NULL);
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeListener(VtnRscListener listener) {
+        checkNotNull(listener, LISTENER_NOT_NULL);
+        listeners.add(listener);
+    }
+
+    @Override
+    public SegmentationId getL3vni(TenantId tenantId) {
+        checkNotNull(tenantId, "tenantId cannot be null");
+        SegmentationId l3vni = l3vniMap.get(tenantId);
+        if (l3vni == null) {
+            long segmentationId = coreService.getIdGenerator(RUNNELOPTOPOIC)
+                    .getNewId();
+            l3vni = SegmentationId.segmentationId(String
+                    .valueOf(segmentationId));
+            l3vniMap.put(tenantId, l3vni);
+        }
+        return l3vni;
+    }
+
+    private class InnerHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent event) {
+            checkNotNull(event, EVENT_NOT_NULL);
+            Host host = event.subject();
+            String ifaceId = host.annotations().value(IFACEID);
+            VirtualPortId hPortId = VirtualPortId.portId(ifaceId);
+            TenantId tenantId = virtualPortService.getPort(hPortId).tenantId();
+            DeviceId deviceId = host.location().deviceId();
+            if (HostEvent.Type.HOST_ADDED == event.type()) {
+                if (isServiceFunction(hPortId)) {
+                    addDeviceIdOfOvsMap(tenantId, deviceId, sffOvsMap);
+                } else {
+                    addDeviceIdOfOvsMap(tenantId, deviceId, classifierOvsMap);
+                }
+            } else if (HostEvent.Type.HOST_REMOVED == event.type()) {
+                if (isLastSFHostOfTenant(host, deviceId, tenantId)) {
+                    removeDeviceIdOfOvsMap(tenantId, deviceId, sffOvsMap);
+                }
+                if (isLastClassifierHostOfTenant(host, deviceId, tenantId)) {
+                    removeDeviceIdOfOvsMap(tenantId, deviceId, classifierOvsMap);
+                }
+            }
+        }
+    }
+
+    private class InnerFloatingIpListener implements FloatingIpListener {
+
+        @Override
+        public void event(FloatingIpEvent event) {
+            checkNotNull(event, EVENT_NOT_NULL);
+            FloatingIp floatingIp = event.subject();
+            if (FloatingIpEvent.Type.FLOATINGIP_PUT == event.type()) {
+                notifyListeners(new VtnRscEvent(
+                                                VtnRscEvent.Type.FLOATINGIP_PUT,
+                                                new VtnRscEventFeedback(
+                                                                        floatingIp)));
+            }
+            if (FloatingIpEvent.Type.FLOATINGIP_DELETE == event.type()) {
+                notifyListeners(new VtnRscEvent(
+                                                VtnRscEvent.Type.FLOATINGIP_DELETE,
+                                                new VtnRscEventFeedback(
+                                                                        floatingIp)));
+            }
+        }
+    }
+
+    private class InnerRouterListener implements RouterListener {
+
+        @Override
+        public void event(RouterEvent event) {
+            checkNotNull(event, EVENT_NOT_NULL);
+            Router router = event.subject();
+            if (RouterEvent.Type.ROUTER_PUT == event.type()) {
+                notifyListeners(new VtnRscEvent(VtnRscEvent.Type.ROUTER_PUT,
+                                                new VtnRscEventFeedback(router)));
+            }
+            if (RouterEvent.Type.ROUTER_DELETE == event.type()) {
+                notifyListeners(new VtnRscEvent(VtnRscEvent.Type.ROUTER_DELETE,
+                                                new VtnRscEventFeedback(router)));
+            }
+        }
+    }
+
+    private class InnerRouterInterfaceListener
+            implements RouterInterfaceListener {
+
+        @Override
+        public void event(RouterInterfaceEvent event) {
+            checkNotNull(event, EVENT_NOT_NULL);
+            RouterInterface routerInterface = event.subject();
+            if (RouterInterfaceEvent.Type.ROUTER_INTERFACE_PUT == event.type()) {
+                notifyListeners(new VtnRscEvent(
+                                                VtnRscEvent.Type.ROUTER_INTERFACE_PUT,
+                                                new VtnRscEventFeedback(
+                                                                        routerInterface)));
+            }
+            if (RouterInterfaceEvent.Type.ROUTER_INTERFACE_DELETE == event
+                    .type()) {
+                notifyListeners(new VtnRscEvent(
+                                                VtnRscEvent.Type.ROUTER_INTERFACE_DELETE,
+                                                new VtnRscEventFeedback(
+                                                                        routerInterface)));
+            }
+        }
+    }
+
+    @Override
+    public Iterator<Device> getClassifierOfTenant(TenantId tenantId) {
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        Set<DeviceId> deviceIdSet = classifierOvsMap.get(tenantId);
+        Set<Device> deviceSet = new HashSet<>();
+        if (deviceIdSet != null) {
+            for (DeviceId deviceId : deviceIdSet) {
+                deviceSet.add(deviceService.getDevice(deviceId));
+            }
+        }
+        return deviceSet.iterator();
+    }
+
+    @Override
+    public Iterator<Device> getSFFOfTenant(TenantId tenantId) {
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        Set<DeviceId> deviceIdSet = sffOvsMap.get(tenantId);
+        Set<Device> deviceSet = new HashSet<>();
+        if (deviceIdSet != null) {
+            for (DeviceId deviceId : deviceIdSet) {
+                deviceSet.add(deviceService.getDevice(deviceId));
+            }
+        }
+        return deviceSet.iterator();
+    }
+
+    @Override
+    public MacAddress getGatewayMac(HostId hostId) {
+        checkNotNull(hostId, "hostId cannot be null");
+        Host host = hostService.getHost(hostId);
+        String ifaceId = host.annotations().value(IFACEID);
+        VirtualPortId hPortId = VirtualPortId.portId(ifaceId);
+        VirtualPort hPort = virtualPortService.getPort(hPortId);
+        SubnetId subnetId = hPort.fixedIps().iterator().next().subnetId();
+        Subnet subnet = subnetService.getSubnet(subnetId);
+        IpAddress gatewayIp = subnet.gatewayIp();
+        Iterable<VirtualPort> virtualPorts = virtualPortService.getPorts();
+        MacAddress macAddress = null;
+        for (VirtualPort port : virtualPorts) {
+            Set<FixedIp> fixedIpSet = port.fixedIps();
+            for (FixedIp fixedIp : fixedIpSet) {
+                if (fixedIp.ip().equals(gatewayIp)) {
+                    macAddress = port.macAddress();
+                }
+            }
+        }
+        return macAddress;
+    }
+
+    @Override
+    public boolean isServiceFunction(VirtualPortId portId) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public DeviceId getSFToSFFMaping(VirtualPortId portId) {
+        checkNotNull(portId, "portId cannot be null");
+        VirtualPort vmPort = virtualPortService.getPort(portId);
+        Set<Host> hostSet = hostService.getHostsByMac(vmPort.macAddress());
+        for (Host host : hostSet) {
+            if (host.annotations().value(IFACEID).equals(vmPort.portId())) {
+                return host.location().deviceId();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Checks whether the last Service Function host of a specific tenant in
+     * this device.
+     *
+     * @param host the host on device
+     * @param deviceId the device identifier
+     * @param tenantId the tenant identifier
+     * @return true or false
+     */
+    private boolean isLastSFHostOfTenant(Host host, DeviceId deviceId,
+                                         TenantId tenantId) {
+        checkNotNull(host, "host cannot be null");
+        checkNotNull(deviceId, DEVICEID_NOT_NULL);
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        Set<Host> hostSet = hostService.getConnectedHosts(deviceId);
+        for (Host h : hostSet) {
+            String ifaceId = h.annotations().value(IFACEID);
+            VirtualPortId hPortId = VirtualPortId.portId(ifaceId);
+            if (virtualPortService.getPort(hPortId).tenantId() != tenantId) {
+                hostSet.remove(h);
+            } else {
+                if (!isServiceFunction(hPortId)) {
+                    hostSet.remove(h);
+                }
+            }
+        }
+        if (hostSet.size() == 1 && hostSet.contains(host)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the last Classifier host of a specific tenant in this
+     * device.
+     *
+     * @param host the host on device
+     * @param deviceId the device identifier
+     * @param tenantId the tenant identifier
+     * @return true or false
+     */
+    private boolean isLastClassifierHostOfTenant(Host host, DeviceId deviceId,
+                                                 TenantId tenantId) {
+        checkNotNull(host, "host cannot be null");
+        checkNotNull(deviceId, DEVICEID_NOT_NULL);
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        Set<Host> hostSet = hostService.getConnectedHosts(deviceId);
+        for (Host h : hostSet) {
+            String ifaceId = h.annotations().value(IFACEID);
+            VirtualPortId hPortId = VirtualPortId.portId(ifaceId);
+            if (virtualPortService.getPort(hPortId).tenantId() != tenantId) {
+                hostSet.remove(h);
+            } else {
+                if (isServiceFunction(hPortId)) {
+                    hostSet.remove(h);
+                }
+            }
+        }
+        if (hostSet.size() == 1 && hostSet.contains(host)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Adds specify Device identifier to OvsMap.
+     *
+     * @param tenantId the tenant identifier
+     * @param deviceId the device identifier
+     * @param ovsMap the instance of map to store device identifier
+     */
+    private void addDeviceIdOfOvsMap(TenantId tenantId,
+                                     DeviceId deviceId,
+                                     EventuallyConsistentMap<TenantId, Set<DeviceId>> ovsMap) {
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        checkNotNull(deviceId, DEVICEID_NOT_NULL);
+        checkNotNull(ovsMap, OVSMAP_NOT_NULL);
+        if (ovsMap.containsKey(tenantId)) {
+            Set<DeviceId> deviceIdSet = ovsMap.get(tenantId);
+            deviceIdSet.add(deviceId);
+            ovsMap.put(tenantId, deviceIdSet);
+        } else {
+            Set<DeviceId> deviceIdSet = new HashSet<>();
+            deviceIdSet.add(deviceId);
+            ovsMap.put(tenantId, deviceIdSet);
+        }
+    }
+
+    /**
+     * Removes specify Device identifier from OvsMap.
+     *
+     * @param tenantId the tenant identifier
+     * @param deviceId the device identifier
+     * @param ovsMap the instance of map to store device identifier
+     */
+    private void removeDeviceIdOfOvsMap(TenantId tenantId,
+                                        DeviceId deviceId,
+                                        EventuallyConsistentMap<TenantId, Set<DeviceId>> ovsMap) {
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        checkNotNull(deviceId, DEVICEID_NOT_NULL);
+        checkNotNull(ovsMap, OVSMAP_NOT_NULL);
+        Set<DeviceId> deviceIdSet = ovsMap.get(tenantId);
+        if (deviceIdSet.size() > 1) {
+            deviceIdSet.remove(deviceId);
+            ovsMap.put(tenantId, deviceIdSet);
+        } else {
+            ovsMap.remove(tenantId);
+        }
+    }
+
+    /**
+     * Notifies specify event to all listeners.
+     *
+     * @param event VtnRsc event
+     */
+    private void notifyListeners(VtnRscEvent event) {
+        checkNotNull(event, EVENT_NOT_NULL);
+        listeners.forEach(listener -> listener.event(event));
+    }
+}
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/package-info.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/package-info.java
new file mode 100644
index 0000000..aaea08b
--- /dev/null
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/service/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/**
+ * Provides implementation of the VtnRsc service.
+ */
+package org.onosproject.vtnrsc.service.impl;