Introduced HostMoveTracker to suspend hosts that moves too frequently
Change-Id: I3037c626657790ce6817feddba7dbbfac203b188
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultHost.java b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
index ec0d9b5..8e6c195 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
@@ -41,6 +41,7 @@
private final VlanId innerVlan;
private final EthType tpid;
private final boolean configured;
+ private final boolean suspended;
// TODO consider moving this constructor to a builder pattern.
/**
@@ -95,7 +96,7 @@
VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
boolean configured, Annotations... annotations) {
this(providerId, id, mac, vlan, locations, ips, VlanId.NONE,
- EthType.EtherType.UNKNOWN.ethType(), configured, annotations);
+ EthType.EtherType.UNKNOWN.ethType(), configured, annotations);
}
/**
@@ -123,6 +124,36 @@
this.configured = configured;
this.innerVlan = innerVlan;
this.tpid = tpid;
+ this.suspended = false;
+ }
+
+ /**
+ * Creates an end-station host using the supplied information.
+ *
+ * @param providerId provider identity
+ * @param id host identifier
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param locations set of host locations
+ * @param ips host IP addresses
+ * @param configured true if configured via NetworkConfiguration
+ * @param innerVlan host inner VLAN identifier
+ * @param tpid outer TPID of a host
+ * @param suspended true if the host is suspended due to policy violation.
+ * @param annotations optional key/value annotations
+ */
+ public DefaultHost(ProviderId providerId, HostId id, MacAddress mac,
+ VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips, VlanId innerVlan,
+ EthType tpid, boolean configured, boolean suspended, Annotations... annotations) {
+ super(providerId, id, annotations);
+ this.mac = mac;
+ this.vlan = vlan;
+ this.locations = new HashSet<>(locations);
+ this.ips = new HashSet<>(ips);
+ this.configured = configured;
+ this.innerVlan = innerVlan;
+ this.tpid = tpid;
+ this.suspended = suspended;
}
@Override
@@ -177,8 +208,14 @@
}
@Override
+ public boolean suspended() {
+ return this.suspended;
+ }
+
+
+ @Override
public int hashCode() {
- return Objects.hash(id, mac, vlan, locations);
+ return Objects.hash(id, mac, vlan, locations, suspended);
}
@Override
@@ -195,7 +232,8 @@
Objects.equals(this.ipAddresses(), other.ipAddresses()) &&
Objects.equals(this.innerVlan, other.innerVlan) &&
Objects.equals(this.tpid, other.tpid) &&
- Objects.equals(this.annotations(), other.annotations());
+ Objects.equals(this.annotations(), other.annotations()) &&
+ Objects.equals(this.suspended, other.suspended);
}
return false;
}
@@ -212,6 +250,7 @@
.add("configured", configured())
.add("innerVlanId", innerVlan())
.add("outerTPID", tpid())
+ .add("suspended", suspended())
.toString();
}
diff --git a/core/api/src/main/java/org/onosproject/net/Host.java b/core/api/src/main/java/org/onosproject/net/Host.java
index 1c2e50a..d4a922e 100644
--- a/core/api/src/main/java/org/onosproject/net/Host.java
+++ b/core/api/src/main/java/org/onosproject/net/Host.java
@@ -73,6 +73,7 @@
/**
* Returns true if configured by NetworkConfiguration.
+ *
* @return configured/learnt dynamically
*/
default boolean configured() {
@@ -98,4 +99,12 @@
}
// TODO: explore capturing list of recent locations to aid in mobility
+ /**
+ * Returns the state of host whether it is in suspended state(offending host due to frequent movement.).
+ *
+ * @return state true if suspended else false.
+ */
+ boolean suspended();
+
+
}
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostEvent.java b/core/api/src/main/java/org/onosproject/net/host/HostEvent.java
index 0ed491f..0fecf2b 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostEvent.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostEvent.java
@@ -48,7 +48,15 @@
/**
* Signifies that a host location has changed.
*/
- HOST_MOVED
+ HOST_MOVED,
+ /**
+ * Signifies that a host is in offending state, eg: frequent host movement.
+ */
+ HOST_SUSPENDED,
+ /**
+ * Signifies that host state in non offending state.
+ */
+ HOST_UNSUSPENDED
}
private Host prevSubject;
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostStore.java b/core/api/src/main/java/org/onosproject/net/host/HostStore.java
index 0b6238b..6d6eb11 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostStore.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostStore.java
@@ -161,4 +161,21 @@
* @param probeMac the source MAC address ONOS uses to probe the host
*/
default void removePendingHostLocation(MacAddress probeMac) {}
+
+ /**
+ * Update the host to suspended state to true
+ * denotes host is in suspended state.
+ *
+ * @param id ID of the host
+ */
+ default void suspend(HostId id){}
+
+ /**
+ * Update the host suspended state to false
+ * denotes host is in unsuspended state.
+ *
+ * @param id ID of the host
+ */
+ default void unsuspend(HostId id){}
+
}
diff --git a/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java b/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
index a88754f..6afb15c 100644
--- a/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
+++ b/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
@@ -62,6 +62,21 @@
public static final String HM_PROBE_RATE = "probeRate";
public static final long HM_PROBE_RATE_DEFAULT = 30000;
+ public static final String HM_HOST_MOVE_TRACKER_ENABLE = "hostMoveTrackerEnabled";
+ public static final boolean HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT = false;
+
+ public static final String HM_HOST_MOVED_THRESHOLD_IN_MILLIS = "hostMoveThresholdInMillis";
+ public static final int HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT = 200000;
+
+ public static final String HM_HOST_MOVE_COUNTER = "hostMoveCounter";
+ public static final int HM_HOST_MOVE_COUNTER_DEFAULT = 3;
+
+ public static final String HM_OFFENDING_HOST_EXPIRY_IN_MINS = "offendingHostExpiryInMins";
+ public static final long HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT = 1;
+
+ public static final String HM_OFFENDING_HOST_THREADS_POOL_SIZE = "offendingHostClearThreadPool";
+ public static final int HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT = 10;
+
public static final String HM_GREEDY_LEARNING_IPV6 = "greedyLearningIpv6";
public static final boolean HM_GREEDY_LEARNING_IPV6_DEFAULT = false;
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
index df25281..3bacd7f 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.net.host.impl;
+import com.google.common.collect.Sets;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
@@ -59,12 +60,19 @@
import org.slf4j.Logger;
import java.util.Dictionary;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onlab.packet.IPv6.getLinkLocalAddress;
+import static org.onlab.util.Tools.get;
import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS;
import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS_DEFAULT;
import static org.onosproject.net.OsgiPropertyConstants.HM_GREEDY_LEARNING_IPV6;
@@ -73,6 +81,17 @@
import static org.onosproject.net.OsgiPropertyConstants.HM_MONITOR_HOSTS_DEFAULT;
import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE;
import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE;
+import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS;
+import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE;
+import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
+
import static org.onosproject.security.AppGuard.checkPermission;
import static org.onosproject.security.AppPermission.Type.HOST_EVENT;
import static org.onosproject.security.AppPermission.Type.HOST_READ;
@@ -89,10 +108,17 @@
HostProviderRegistry.class
},
property = {
- HM_ALLOW_DUPLICATE_IPS + ":Boolean=" + HM_ALLOW_DUPLICATE_IPS_DEFAULT,
- HM_MONITOR_HOSTS + ":Boolean=" + HM_MONITOR_HOSTS_DEFAULT,
- HM_PROBE_RATE + ":Integer=" + HM_PROBE_RATE_DEFAULT,
- HM_GREEDY_LEARNING_IPV6 + ":Boolean=" + HM_GREEDY_LEARNING_IPV6_DEFAULT
+ HM_ALLOW_DUPLICATE_IPS + ":Boolean=" + HM_ALLOW_DUPLICATE_IPS_DEFAULT,
+ HM_MONITOR_HOSTS + ":Boolean=" + HM_MONITOR_HOSTS_DEFAULT,
+ HM_PROBE_RATE + ":Integer=" + HM_PROBE_RATE_DEFAULT,
+ HM_GREEDY_LEARNING_IPV6 + ":Boolean=" + HM_GREEDY_LEARNING_IPV6_DEFAULT,
+ HM_HOST_MOVE_TRACKER_ENABLE + ":Boolean=" + HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT,
+ HM_HOST_MOVED_THRESHOLD_IN_MILLIS + ":Integer=" + HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT,
+ HM_HOST_MOVE_COUNTER + ":Integer=" + HM_HOST_MOVE_COUNTER_DEFAULT,
+ HM_OFFENDING_HOST_EXPIRY_IN_MINS + ":Long=" + HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT,
+ HM_OFFENDING_HOST_THREADS_POOL_SIZE + ":Integer=" + HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT
+
+
}
)
public class HostManager
@@ -140,8 +166,25 @@
/** Enable/Disable greedy learning of IPv6 link local address. */
private boolean greedyLearningIpv6 = HM_GREEDY_LEARNING_IPV6_DEFAULT;
+ /** Enable/Disable tracking of rogue host moves. */
+ private boolean hostMoveTrackerEnabled = HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT;
+
+ /** Host move threshold in milli seconds. */
+ private int hostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
+
+ /** If the host move happening within given threshold then increment the host move counter. */
+ private int hostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT;
+
+ /** Max value of the counter after which the host will not be considered as offending host. */
+ private long offendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
+
+ /** Default pool size of offending host clear executor thread. */
+ private int offendingHostClearThreadPool = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
+
private HostMonitor monitor;
private HostAnnotationOperator hostAnnotationOperator;
+ private ScheduledExecutorService offendingHostUnblockExecutor = null;
+ private Map<HostId, HostMoveTracker> hostMoveTracker = new ConcurrentHashMap<>();
@Activate
@@ -154,8 +197,8 @@
monitor = new HostMonitor(packetService, this, interfaceService, edgePortService);
monitor.setProbeRate(probeRate);
monitor.start();
- modified(context);
cfgService.registerProperties(getClass());
+ modified(context);
log.info("Started");
}
@@ -166,6 +209,9 @@
networkConfigService.removeListener(networkConfigListener);
cfgService.unregisterProperties(getClass(), false);
monitor.shutdown();
+ if (offendingHostUnblockExecutor != null) {
+ offendingHostUnblockExecutor.shutdown();
+ }
log.info("Stopped");
}
@@ -196,6 +242,10 @@
private void readComponentConfiguration(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
Boolean flag;
+ int newHostMoveThresholdInMillis;
+ int newHostMoveCounter;
+ int newOffendinghostPoolSize;
+ long newOffendingHostExpiryInMins;
flag = Tools.isPropertyEnabled(properties, HM_MONITOR_HOSTS);
if (flag == null) {
@@ -233,7 +283,71 @@
log.info("Configured. greedyLearningIpv6 {}",
greedyLearningIpv6 ? "enabled" : "disabled");
}
+ flag = Tools.isPropertyEnabled(properties, HM_HOST_MOVE_TRACKER_ENABLE);
+ if (flag == null) {
+ log.info("Host move tracker is not configured " +
+ "using current value of {}", hostMoveTrackerEnabled);
+ } else {
+ hostMoveTrackerEnabled = flag;
+ log.info("Configured. hostMoveTrackerEnabled {}",
+ hostMoveTrackerEnabled ? "enabled" : "disabled");
+ //On enable cfg ,sets default configuration vales added , else use the default values
+ properties = context.getProperties();
+ try {
+ String s = get(properties, HM_HOST_MOVED_THRESHOLD_IN_MILLIS);
+ newHostMoveThresholdInMillis = isNullOrEmpty(s) ?
+ hostMoveThresholdInMillis : Integer.parseInt(s.trim());
+
+ s = get(properties, HM_HOST_MOVE_COUNTER);
+ newHostMoveCounter = isNullOrEmpty(s) ? hostMoveCounter : Integer.parseInt(s.trim());
+
+ s = get(properties, HM_OFFENDING_HOST_EXPIRY_IN_MINS);
+ newOffendingHostExpiryInMins = isNullOrEmpty(s) ?
+ offendingHostExpiryInMins : Integer.parseInt(s.trim());
+
+ s = get(properties, HM_OFFENDING_HOST_THREADS_POOL_SIZE);
+ newOffendinghostPoolSize = isNullOrEmpty(s) ?
+ offendingHostClearThreadPool : Integer.parseInt(s.trim());
+ } catch (NumberFormatException | ClassCastException e) {
+ newHostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
+ newHostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT;
+ newOffendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
+ newOffendinghostPoolSize = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
+ }
+ if (newHostMoveThresholdInMillis != hostMoveThresholdInMillis) {
+ hostMoveThresholdInMillis = newHostMoveThresholdInMillis;
+ }
+ if (newHostMoveCounter != hostMoveCounter) {
+ hostMoveCounter = newHostMoveCounter;
+ }
+ if (newOffendingHostExpiryInMins != offendingHostExpiryInMins) {
+ offendingHostExpiryInMins = newOffendingHostExpiryInMins;
+ }
+ if (hostMoveTrackerEnabled && offendingHostUnblockExecutor == null) {
+ setupThreadPool();
+ } else if (newOffendinghostPoolSize != offendingHostClearThreadPool
+ && offendingHostUnblockExecutor != null) {
+ offendingHostClearThreadPool = newOffendinghostPoolSize;
+ offendingHostUnblockExecutor.shutdown();
+ offendingHostUnblockExecutor = null;
+ setupThreadPool();
+ } else if (!hostMoveTrackerEnabled && offendingHostUnblockExecutor != null) {
+ offendingHostUnblockExecutor.shutdown();
+ offendingHostUnblockExecutor = null;
+ }
+ if (newOffendinghostPoolSize != offendingHostClearThreadPool) {
+ offendingHostClearThreadPool = newOffendinghostPoolSize;
+ }
+
+ log.debug("modified hostMoveThresholdInMillis: {}, hostMoveCounter: {}, " +
+ "offendingHostExpiryInMins: {} ", hostMoveThresholdInMillis,
+ hostMoveCounter, offendingHostExpiryInMins);
+ }
+ }
+
+ private synchronized void setupThreadPool() {
+ offendingHostUnblockExecutor = Executors.newScheduledThreadPool(offendingHostClearThreadPool);
}
/**
@@ -371,8 +485,16 @@
hostDescription = hostAnnotationOperator.combine(hostId, hostDescription, Optional.of(annoConfig));
}
- store.createOrUpdateHost(provider().id(), hostId,
- hostDescription, replaceIps);
+ if (!hostMoveTrackerEnabled) {
+ store.createOrUpdateHost(provider().id(), hostId,
+ hostDescription, replaceIps);
+ } else if (!shouldBlock(hostId, hostDescription.locations())) {
+ log.debug("Host move is allowed for host with Id: {} ", hostId);
+ store.createOrUpdateHost(provider().id(), hostId,
+ hostDescription, replaceIps);
+ } else {
+ log.info("Host move is NOT allowed for host with Id: {} , removing from host store ", hostId);
+ }
if (monitorHosts) {
hostDescription.ipAddress().forEach(ip -> {
@@ -424,7 +546,7 @@
allHosts.forEach(eachHost -> {
if (!(eachHost.id().equals(hostId))) {
log.info("Duplicate ip {} found on host {} and {}", ip,
- hostId.toString(), eachHost.id().toString());
+ hostId.toString(), eachHost.id().toString());
store.removeIp(eachHost.id(), ip);
}
});
@@ -509,8 +631,83 @@
Host host = store.getHost(hostId);
return host == null || !host.configured() || host.providerId().equals(provider().id());
}
+
+
+ /**
+ * Deny host move if happening within the threshold time,
+ * track moved host to identify offending hosts.
+ *
+ * @param hostId host identifier
+ * @param locations host locations
+ */
+ private boolean shouldBlock(HostId hostId, Set<HostLocation> locations) {
+ Host host = store.getHost(hostId);
+ // If host is not present in host store means host added for hte first time.
+ if (host != null) {
+ if (host.suspended()) {
+ // Checks host is marked as offending in other onos cluster instance/local instance
+ log.debug("Host id {} is moving frequently hence host moving " +
+ "processing is ignored", hostId);
+ return true;
+ }
+ } else {
+ //host added for the first time.
+ return false;
+ }
+ HostMoveTracker hostMove = hostMoveTracker.computeIfAbsent(hostId, id -> new HostMoveTracker(locations));
+ if (Sets.difference(hostMove.getLocations(), locations).isEmpty() &&
+ Sets.difference(locations, hostMove.getLocations()).isEmpty()) {
+ log.debug("Not hostmove scenario: Host id: {}, Old Host Location: {}, New host Location: {}",
+ hostId, hostMove.getLocations(), locations);
+ return false; // It is not a host move scenario
+ } else if (hostMove.getCounter() >= hostMoveCounter && System.currentTimeMillis() - hostMove.getTimeStamp()
+ < hostMoveThresholdInMillis) {
+ //Check host move is crossed the threshold, then to mark as offending Host
+ log.debug("Host id {} is identified as offending host and entry is added in cache", hostId);
+ hostMove.resetHostMoveTracker(locations);
+ store.suspend(hostId);
+ //Set host suspended flag to false after given offendingHostExpiryInMins
+ offendingHostUnblockExecutor.schedule(new UnblockOffendingHost(hostId),
+ offendingHostExpiryInMins,
+ TimeUnit.MINUTES);
+ return true;
+ } else if (System.currentTimeMillis() - hostMove.getTimeStamp()
+ < hostMoveThresholdInMillis) {
+ //Increment the host move count as hostmove occured within the hostMoveThresholdInMillis time
+ hostMove.updateHostMoveTracker(locations);
+ log.debug("Updated the tracker with the host move registered for host: {}", hostId);
+ } else if (System.currentTimeMillis() - hostMove.getTimeStamp()
+ > hostMoveThresholdInMillis) {
+ //Hostmove is happened after hostMoveThresholdInMillis time so remove from host tracker.
+ hostMove.resetHostMoveTracker(locations);
+ store.unsuspend(hostId);
+ log.debug("Reset the tracker with the host move registered for host: {}", hostId);
+ }
+ return false;
+ }
+
+ // Set host suspended flag to false after given offendingHostExpiryInMins.
+ private final class UnblockOffendingHost implements Runnable {
+ private HostId hostId;
+
+ UnblockOffendingHost(HostId hostId) {
+ this.hostId = hostId;
+ }
+
+ @Override
+ public void run() {
+ // Set the host suspended flag to false
+ try {
+ store.unsuspend(hostId);
+ log.debug("Host {}: Marked host as unsuspended", hostId);
+ } catch (Exception ex) {
+ log.debug("Host {}: not present in host list", hostId);
+ }
+ }
+ }
}
+
// Store delegate to re-post events emitted from the store.
private class InternalStoreDelegate implements HostStoreDelegate {
@Override
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostMoveTracker.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostMoveTracker.java
new file mode 100644
index 0000000..05a8f77
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostMoveTracker.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015-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.net.host.impl;
+
+import org.onosproject.net.HostLocation;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Used for tracking of the host move.
+ */
+public class HostMoveTracker {
+ private Integer counter;
+ private Long timeStamp;
+ private Set<HostLocation> locations;
+
+ /**
+ * Initialize the instance of HostMoveTracker.
+ *
+ * @param locations List of locations where host is present
+ */
+ public HostMoveTracker(Set<HostLocation> locations) {
+ counter = 1;
+ timeStamp = System.currentTimeMillis();
+ this.locations = locations;
+ }
+
+ /**
+ * Updates locations in HostMoveTracker.
+ *
+ * @param locations List of locations where host is present
+ */
+ public void updateHostMoveTracker(Set<HostLocation> locations) {
+ counter += 1;
+ timeStamp = System.currentTimeMillis();
+ this.locations = locations;
+ }
+
+ /**
+ * Reset hostmove count,timestamp and updated locations.
+ *
+ * @param locations List of locations where host is present
+ */
+ public void resetHostMoveTracker(Set<HostLocation> locations) {
+ counter = 0;
+ timeStamp = System.currentTimeMillis();
+ this.locations = locations;
+ }
+
+ /**
+ * Reset hostmove count and timestamp.
+ */
+ public void resetHostMoveTracker() {
+ counter = 0;
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public Long getTimeStamp() {
+ return timeStamp;
+ }
+
+ public Integer getCounter() {
+ return counter;
+ }
+
+
+ public Set<HostLocation> getLocations() {
+ return locations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ HostMoveTracker that = (HostMoveTracker) o;
+ return Objects.equals(locations, that.locations) &&
+ Objects.equals(counter, that.counter);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return Objects.hash(locations, counter);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("counter", getCounter())
+ .add("timeStamp", getTimeStamp())
+ .add("locations", getLocations())
+ .toString();
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
index 16ad729..8cf463d 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
@@ -37,11 +37,11 @@
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.DistributedPrimitive.Status;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.DistributedPrimitive.Status;
import org.onosproject.store.service.Versioned;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -66,10 +66,7 @@
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.DefaultAnnotations.merge;
-import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
-import static org.onosproject.net.host.HostEvent.Type.HOST_MOVED;
-import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
-import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED;
+import static org.onosproject.net.host.HostEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -164,7 +161,7 @@
if (replaceIPs) {
if (!Objects.equals(hostDescription.ipAddress(),
- existingHost.ipAddresses())) {
+ existingHost.ipAddresses())) {
return true;
}
} else {
@@ -176,8 +173,8 @@
// check to see if any of the annotations provided by hostDescription
// differ from those in the existing host
return hostDescription.annotations().keys().stream()
- .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
- existingHost.annotations().value(k)));
+ .anyMatch(k -> !Objects.equals(hostDescription.annotations().value(k),
+ existingHost.annotations().value(k)));
}
@@ -189,37 +186,37 @@
HostDescription hostDescription,
boolean replaceIPs) {
hostsConsistentMap.computeIf(hostId,
- existingHost -> shouldUpdate(existingHost, providerId,
- hostDescription, replaceIPs),
- (id, existingHost) -> {
+ existingHost -> shouldUpdate(existingHost, providerId,
+ hostDescription, replaceIPs),
+ (id, existingHost) -> {
- final Set<IpAddress> addresses;
- if (existingHost == null || replaceIPs) {
- addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
- } else {
- addresses = Sets.newHashSet(existingHost.ipAddresses());
- addresses.addAll(hostDescription.ipAddress());
- }
+ final Set<IpAddress> addresses;
+ if (existingHost == null || replaceIPs) {
+ addresses = ImmutableSet.copyOf(hostDescription.ipAddress());
+ } else {
+ addresses = Sets.newHashSet(existingHost.ipAddresses());
+ addresses.addAll(hostDescription.ipAddress());
+ }
- final Annotations annotations;
- if (existingHost != null) {
- annotations = merge((DefaultAnnotations) existingHost.annotations(),
- hostDescription.annotations());
- } else {
- annotations = hostDescription.annotations();
- }
+ final Annotations annotations;
+ if (existingHost != null) {
+ annotations = merge((DefaultAnnotations) existingHost.annotations(),
+ hostDescription.annotations());
+ } else {
+ annotations = hostDescription.annotations();
+ }
- return new DefaultHost(providerId,
- hostId,
- hostDescription.hwAddress(),
- hostDescription.vlan(),
- hostDescription.locations(),
- addresses,
- hostDescription.innerVlan(),
- hostDescription.tpid(),
- hostDescription.configured(),
- annotations);
- });
+ return new DefaultHost(providerId,
+ hostId,
+ hostDescription.hwAddress(),
+ hostDescription.vlan(),
+ hostDescription.locations(),
+ addresses,
+ hostDescription.innerVlan(),
+ hostDescription.tpid(),
+ hostDescription.configured(),
+ annotations);
+ });
return null;
}
@@ -279,9 +276,9 @@
.forEach(newLocations::add);
return new DefaultHost(existingHost.providerId(),
- hostId, existingHost.mac(), existingHost.vlan(),
- newLocations, existingHost.ipAddresses(),
- existingHost.configured(), existingHost.annotations());
+ hostId, existingHost.mac(), existingHost.vlan(),
+ newLocations, existingHost.ipAddresses(),
+ existingHost.configured(), existingHost.annotations());
}
return null;
});
@@ -361,6 +358,52 @@
return ImmutableSet.copyOf(filtered);
}
+ @Override
+ public void suspend(HostId hostId) {
+ hosts.compute(hostId, (id, existingHost) -> {
+ if (existingHost != null) {
+ if (!existingHost.suspended()) {
+ return new DefaultHost(existingHost.providerId(),
+ hostId,
+ existingHost.mac(),
+ existingHost.vlan(),
+ existingHost.locations(),
+ existingHost.ipAddresses(),
+ existingHost.innerVlan(),
+ existingHost.tpid(),
+ existingHost.configured(),
+ true,
+ existingHost.annotations());
+ }
+
+ }
+ return null;
+ });
+ }
+
+ @Override
+ public void unsuspend(HostId hostId) {
+ hosts.compute(hostId, (id, existingHost) -> {
+ if (existingHost != null) {
+ if (existingHost.suspended()) {
+ return new DefaultHost(existingHost.providerId(),
+ hostId,
+ existingHost.mac(),
+ existingHost.vlan(),
+ existingHost.locations(),
+ existingHost.ipAddresses(),
+ existingHost.innerVlan(),
+ existingHost.tpid(),
+ existingHost.configured(),
+ false,
+ existingHost.annotations());
+
+ }
+ }
+ return null;
+ });
+ }
+
private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
return collection.stream().filter(predicate).collect(Collectors.toSet());
}
@@ -418,7 +461,11 @@
break;
case UPDATE:
updateHostsByIp(host, prevHost);
- if (!Objects.equals(prevHost.locations(), host.locations())) {
+ if (host.suspended() && !prevHost.suspended()) {
+ notifyDelegate(new HostEvent(HOST_SUSPENDED, host, prevHost));
+ } else if (!host.suspended() && prevHost.suspended()) {
+ notifyDelegate(new HostEvent(HOST_UNSUSPENDED, host, prevHost));
+ } else if (!Objects.equals(prevHost.locations(), host.locations())) {
notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
} else if (!Objects.equals(prevHost, host)) {
notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));