[ONOS-2820] add implementation of floatingIpService api

Change-Id: I65a2643dcf0443fa4ae0cd647bcb42fa9e2a6e44
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFloatingIp.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFloatingIp.java
new file mode 100644
index 0000000..7a297d9
--- /dev/null
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFloatingIp.java
@@ -0,0 +1,140 @@
+/*
+ * 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;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * Default implementation of FloatingIp interface.
+ */
+public final class DefaultFloatingIp implements FloatingIp {
+
+    private final FloatingIpId id;
+    private final TenantId tenantId;
+    private final TenantNetworkId networkId;
+    private final VirtualPortId portId;
+    private final RouterId routerId;
+    private final IpAddress floatingIp;
+    private final IpAddress fixedIp;
+    private final Status status;
+
+    /**
+     *
+     * Creates a floating Ip object.
+     *
+     * @param id  floatingIp identifier
+     * @param tenantId  tenant identifier
+     * @param networkId  the identifier of network associated with the floating Ip
+     * @param portId  port identifier
+     * @param routerId  router identifier
+     * @param floatingIp  floatingIp address
+     * @param fixedIp  the fixed Ip associated with the floating Ip
+     * @param status  the floating Ip status
+     */
+    public DefaultFloatingIp(FloatingIpId id, TenantId tenantId,
+                             TenantNetworkId networkId, VirtualPortId portId,
+                             RouterId routerId, IpAddress floatingIp,
+                             IpAddress fixedIp, Status status) {
+        this.id = checkNotNull(id, "id cannot be null");
+        this.tenantId = checkNotNull(tenantId, "tenantId cannot be null");
+        this.networkId = checkNotNull(networkId, "networkId cannot be null");
+        this.portId = portId;
+        this.routerId = routerId;
+        this.floatingIp = checkNotNull(floatingIp, "floatingIp cannot be null");
+        this.fixedIp = fixedIp;
+        this.status = checkNotNull(status, "status cannot be null");
+    }
+
+    @Override
+    public FloatingIpId id() {
+        return id;
+    }
+
+    @Override
+    public TenantId tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public TenantNetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public VirtualPortId portId() {
+        return portId;
+    }
+
+    @Override
+    public RouterId routerId() {
+        return routerId;
+    }
+
+    @Override
+    public IpAddress floatingIp() {
+        return floatingIp;
+    }
+
+    @Override
+    public IpAddress fixedIp() {
+        return fixedIp;
+    }
+
+    @Override
+    public Status status() {
+        return status;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, tenantId, networkId, portId, routerId,
+                            floatingIp, fixedIp, status);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultFloatingIp) {
+            final DefaultFloatingIp that = (DefaultFloatingIp) obj;
+            return Objects.equals(this.id, that.id)
+                    && Objects.equals(this.tenantId, that.tenantId)
+                    && Objects.equals(this.networkId, that.networkId)
+                    && Objects.equals(this.portId, that.portId)
+                    && Objects.equals(this.routerId, that.routerId)
+                    && Objects.equals(this.floatingIp, that.floatingIp)
+                    && Objects.equals(this.fixedIp, that.fixedIp)
+                    && Objects.equals(this.status, that.status);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("id", id).add("tenantId", tenantId)
+                .add("networkId", networkId).add("portId", portId)
+                .add("routerId", routerId).add("floatingIp", floatingIp)
+                .add("fixedIp", fixedIp).add("floatingIpStatus", status)
+                .toString();
+    }
+
+}
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/impl/FloatingIpManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/impl/FloatingIpManager.java
new file mode 100644
index 0000000..9f944da
--- /dev/null
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/impl/FloatingIpManager.java
@@ -0,0 +1,348 @@
+/*
+ * 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.floatingip.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collection;
+import java.util.Collections;
+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.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtnrsc.DefaultFloatingIp;
+import org.onosproject.vtnrsc.FloatingIp;
+import org.onosproject.vtnrsc.FloatingIpId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.RouterId;
+import org.onosproject.vtnrsc.floatingip.FloatingIpEvent;
+import org.onosproject.vtnrsc.floatingip.FloatingIpListener;
+import org.onosproject.vtnrsc.floatingip.FloatingIpService;
+import org.onosproject.vtnrsc.router.RouterService;
+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 FloatingIp service.
+ */
+@Component(immediate = true)
+@Service
+public class FloatingIpManager implements FloatingIpService {
+    private static final String FLOATINGIP_ID_NOT_NULL = "Floatingip ID cannot be null";
+    private static final String FLOATINGIP_NOT_NULL = "Floatingip cannot be null";
+    private static final String FLOATINGIP = "vtn-floatingip-store";
+    private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+    private static final String LISTENER_NOT_NULL = "Listener cannot be null";
+    private static final String EVENT_NOT_NULL = "event cannot be null";
+
+    private final Logger log = getLogger(getClass());
+    private final Set<FloatingIpListener> listeners = Sets
+            .newCopyOnWriteArraySet();
+    private EventuallyConsistentMapListener<FloatingIpId, FloatingIp> floatingIpListener =
+            new InnerFloatingIpStoreListener();
+    protected EventuallyConsistentMap<FloatingIpId, FloatingIp> floatingIpStore;
+    protected ApplicationId appId;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TenantNetworkService tenantNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualPortService virtualPortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RouterService routerService;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication(VTNRSC_APP);
+        KryoNamespace.Builder serializer = KryoNamespace
+                .newBuilder()
+                .register(KryoNamespaces.API)
+                .register(FloatingIp.class, FloatingIpId.class,
+                          TenantNetworkId.class, TenantId.class,
+                          FloatingIp.Status.class, RouterId.class,
+                          VirtualPortId.class, DefaultFloatingIp.class);
+        floatingIpStore = storageService
+                .<FloatingIpId, FloatingIp>eventuallyConsistentMapBuilder()
+                .withName(FLOATINGIP).withSerializer(serializer)
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+        floatingIpStore.addListener(floatingIpListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        floatingIpStore.removeListener(floatingIpListener);
+        floatingIpStore.destroy();
+        listeners.clear();
+        log.info("Stopped");
+    }
+
+    @Override
+    public Collection<FloatingIp> getFloatingIps() {
+        return Collections.unmodifiableCollection(floatingIpStore.values());
+    }
+
+    @Override
+    public FloatingIp getFloatingIp(FloatingIpId floatingIpId) {
+        checkNotNull(floatingIpId, FLOATINGIP_ID_NOT_NULL);
+        return floatingIpStore.get(floatingIpId);
+    }
+
+    @Override
+    public boolean exists(FloatingIpId floatingIpId) {
+        checkNotNull(floatingIpId, FLOATINGIP_ID_NOT_NULL);
+        return floatingIpStore.containsKey(floatingIpId);
+    }
+
+    @Override
+    public boolean floatingIpIsUsed(IpAddress floatingIpAddr,
+                                    FloatingIpId floatingIpId) {
+        checkNotNull(floatingIpAddr, "Floating IP address cannot be null");
+        checkNotNull(floatingIpId, "Floating IP Id cannot be null");
+        Collection<FloatingIp> floatingIps = getFloatingIps();
+        for (FloatingIp floatingIp : floatingIps) {
+            if (floatingIp.floatingIp().equals(floatingIpAddr)
+                    && !floatingIp.id().equals(floatingIpId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean fixedIpIsUsed(IpAddress fixedIpAddr, TenantId tenantId,
+                                 FloatingIpId floatingIpId) {
+        checkNotNull(fixedIpAddr, "Fixed IP address cannot be null");
+        checkNotNull(tenantId, "Tenant Id cannot be null");
+        checkNotNull(floatingIpId, "Floating IP Id cannot be null");
+        Collection<FloatingIp> floatingIps = getFloatingIps();
+        for (FloatingIp floatingIp : floatingIps) {
+            IpAddress fixedIp = floatingIp.fixedIp();
+            if (fixedIp != null) {
+                if (fixedIp.equals(fixedIpAddr)
+                        && floatingIp.tenantId().equals(tenantId)
+                        && !floatingIp.id().equals(floatingIpId)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean createFloatingIps(Collection<FloatingIp> floatingIps) {
+        checkNotNull(floatingIps, FLOATINGIP_NOT_NULL);
+        boolean result = true;
+        for (FloatingIp floatingIp : floatingIps) {
+            verifyFloatingIpData(floatingIp);
+            if (floatingIp.portId() != null) {
+                floatingIpStore.put(floatingIp.id(), floatingIp);
+                if (!floatingIpStore.containsKey(floatingIp.id())) {
+                    log.debug("The floating Ip is created failed whose identifier is {}",
+                              floatingIp.id().toString());
+                    result = false;
+                }
+            } else {
+                FloatingIp oldFloatingIp = floatingIpStore.get(floatingIp.id());
+                if (oldFloatingIp != null) {
+                    floatingIpStore.remove(floatingIp.id(), oldFloatingIp);
+                    if (floatingIpStore.containsKey(floatingIp.id())) {
+                        log.debug("The floating Ip is created failed whose identifier is {}",
+                                  floatingIp.id().toString());
+                        result = false;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public boolean updateFloatingIps(Collection<FloatingIp> floatingIps) {
+        checkNotNull(floatingIps, FLOATINGIP_NOT_NULL);
+        boolean result = true;
+        if (floatingIps != null) {
+            for (FloatingIp floatingIp : floatingIps) {
+                verifyFloatingIpData(floatingIp);
+                if (floatingIp.portId() != null) {
+                    floatingIpStore.put(floatingIp.id(), floatingIp);
+                    if (!floatingIpStore.containsKey(floatingIp.id())) {
+                        log.debug("The floating Ip is updated failed whose identifier is {}",
+                                  floatingIp.id().toString());
+                        result = false;
+                    }
+                } else {
+                    FloatingIp oldFloatingIp = floatingIpStore.get(floatingIp
+                            .id());
+                    if (oldFloatingIp != null) {
+                        floatingIpStore.remove(floatingIp.id(), oldFloatingIp);
+                        if (floatingIpStore.containsKey(floatingIp.id())) {
+                            log.debug("The floating Ip is updated failed whose identifier is {}",
+                                      floatingIp.id().toString());
+                            result = false;
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public boolean removeFloatingIps(Collection<FloatingIpId> floatingIpIds) {
+        checkNotNull(floatingIpIds, FLOATINGIP_ID_NOT_NULL);
+        boolean result = true;
+        if (floatingIpIds != null) {
+            for (FloatingIpId floatingIpId : floatingIpIds) {
+                if (!floatingIpStore.containsKey(floatingIpId)) {
+                    log.debug("The floatingIp is not exist whose identifier is {}",
+                              floatingIpId.toString());
+                    throw new IllegalArgumentException(
+                                                       "FloatingIP ID doesn't exist");
+                }
+                FloatingIp floatingIp = floatingIpStore.get(floatingIpId);
+                floatingIpStore.remove(floatingIpId, floatingIp);
+                if (floatingIpStore.containsKey(floatingIpId)) {
+                    log.debug("The floating Ip is deleted failed whose identifier is {}",
+                              floatingIpId.toString());
+                    result = false;
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void addListener(FloatingIpListener listener) {
+        checkNotNull(listener, LISTENER_NOT_NULL);
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeListener(FloatingIpListener listener) {
+        checkNotNull(listener, LISTENER_NOT_NULL);
+        listeners.add(listener);
+    }
+
+    /**
+     * Verifies validity of FloatingIp data.
+     *
+     * @param floatingIps floatingIp instance
+     */
+    private void verifyFloatingIpData(FloatingIp floatingIps) {
+        checkNotNull(floatingIps, FLOATINGIP_NOT_NULL);
+        if (!tenantNetworkService.exists(floatingIps.networkId())) {
+            log.debug("The network identifier {} that the floating Ip {} create for is not exist",
+                      floatingIps.networkId().toString(), floatingIps.id()
+                              .toString());
+            throw new IllegalArgumentException(
+                                               "Floating network ID doesn't exist");
+        }
+
+        VirtualPortId portId = floatingIps.portId();
+        if (portId != null && !virtualPortService.exists(portId)) {
+            log.debug("The port identifier {} that the floating Ip {} create for is not exist",
+                      floatingIps.portId().toString(), floatingIps.id()
+                              .toString());
+            throw new IllegalArgumentException("Port ID doesn't exist");
+        }
+
+        RouterId routerId = floatingIps.routerId();
+        if (routerId != null && !routerService.exists(routerId)) {
+            log.debug("The router identifier {} that the floating Ip {} create for is not exist",
+                      floatingIps.routerId().toString(), floatingIps.id()
+                              .toString());
+            throw new IllegalArgumentException("Router ID doesn't exist");
+        }
+
+        if (floatingIpIsUsed(floatingIps.floatingIp(), floatingIps.id())) {
+            log.debug("The floaing Ip {} that the floating Ip {} create for is used",
+                      floatingIps.floatingIp().toString(), floatingIps.id()
+                              .toString());
+            throw new IllegalArgumentException(
+                                               "The floating IP address is used");
+        }
+
+        IpAddress fixedIp = floatingIps.fixedIp();
+        if (fixedIp != null
+                && fixedIpIsUsed(fixedIp, floatingIps.tenantId(),
+                                 floatingIps.id())) {
+            log.debug("The fixed Ip {} that the floating Ip {} create for is used",
+                      floatingIps.fixedIp().toString(), floatingIps.id()
+                              .toString());
+            throw new IllegalArgumentException("The fixed IP address is used");
+        }
+    }
+
+    private class InnerFloatingIpStoreListener
+            implements
+            EventuallyConsistentMapListener<FloatingIpId, FloatingIp> {
+
+        @Override
+        public void event(EventuallyConsistentMapEvent<FloatingIpId, FloatingIp> event) {
+            checkNotNull(event, EVENT_NOT_NULL);
+            FloatingIp floatingIp = event.value();
+            if (EventuallyConsistentMapEvent.Type.PUT == event.type()) {
+                notifyListeners(new FloatingIpEvent(
+                                                    FloatingIpEvent.Type.FLOATINGIP_PUT,
+                                                    floatingIp));
+            }
+            if (EventuallyConsistentMapEvent.Type.REMOVE == event.type()) {
+                notifyListeners(new FloatingIpEvent(
+                                                    FloatingIpEvent.Type.FLOATINGIP_DELETE,
+                                                    floatingIp));
+            }
+        }
+    }
+
+    /**
+     * Notifies specify event to all listeners.
+     *
+     * @param event Floating IP event
+     */
+    private void notifyListeners(FloatingIpEvent event) {
+        checkNotNull(event, EVENT_NOT_NULL);
+        listeners.forEach(listener -> listener.event(event));
+    }
+}
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/impl/package-info.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/impl/package-info.java
new file mode 100644
index 0000000..c638eba
--- /dev/null
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/floatingip/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 FloatingIp service.
+ */
+package org.onosproject.vtnrsc.floatingip.impl;