Madan Jampani

Preliminary checkin for implementing version aware topology state.

......@@ -16,6 +16,7 @@ import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.MastershipEvent;
import org.onlab.onos.cluster.MastershipListener;
import org.onlab.onos.cluster.MastershipService;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.Device;
......@@ -36,6 +37,7 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.onlab.onos.store.common.ClockService;
import org.slf4j.Logger;
/**
......@@ -74,6 +76,9 @@ implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClockService clockService;
@Activate
public void activate() {
store.setDelegate(delegate);
......@@ -250,6 +255,8 @@ implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
@Override
public void event(MastershipEvent event) {
if (event.master().equals(clusterService.getLocalNode().id())) {
MastershipTerm term = mastershipService.requestTermService().getMastershipTerm(event.subject());
clockService.setMastershipTerm(event.subject(), term);
applyRole(event.subject(), MastershipRole.MASTER);
} else {
applyRole(event.subject(), MastershipRole.STANDBY);
......
package org.onlab.onos.store.common;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
/**
* Interface for a logical clock service that vends per device timestamps.
*/
public interface ClockService {
/**
* Returns a new timestamp for the specified deviceId.
* @param deviceId device identifier.
* @return timestamp.
*/
public Timestamp getTimestamp(DeviceId deviceId);
/**
* Updates the mastership term for the specified deviceId.
* @param deviceId device identifier.
* @param term mastership term.
*/
public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
}
package org.onlab.onos.store.device.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
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.Service;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.common.ClockService;
import org.onlab.onos.store.impl.OnosTimestamp;
import org.slf4j.Logger;
@Component(immediate = true)
@Service
public class OnosClockService implements ClockService {
private final Logger log = getLogger(getClass());
// TODO: Implement per device ticker that is reset to 0 at the beginning of a new term.
private final AtomicInteger ticker = new AtomicInteger(0);
private ConcurrentMap<DeviceId, MastershipTerm> deviceMastershipTerms = new ConcurrentHashMap<>();
@Activate
public void activate() {
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public Timestamp getTimestamp(DeviceId deviceId) {
MastershipTerm term = deviceMastershipTerms.get(deviceId);
if (term == null) {
throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
}
return new OnosTimestamp(term.termNumber(), ticker.incrementAndGet());
}
@Override
public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
deviceMastershipTerms.put(deviceId, term);
}
}
package org.onlab.onos.store.device.impl;
import org.onlab.onos.store.Timestamp;
/**
* Wrapper class for a entity that is versioned
* and can either be up or down.
*
* @param <T> type of the value.
*/
public class VersionedValue<T> {
private final T entity;
private final Timestamp timestamp;
private final boolean isUp;
public VersionedValue(T entity, boolean isUp, Timestamp timestamp) {
this.entity = entity;
this.isUp = isUp;
this.timestamp = timestamp;
}
/**
* Returns the value.
* @return value.
*/
public T entity() {
return entity;
}
/**
* Tells whether the entity is up or down.
* @return true if up, false otherwise.
*/
public boolean isUp() {
return isUp;
}
/**
* Returns the timestamp (version) associated with this entity.
* @return timestamp.
*/
public Timestamp timestamp() {
return timestamp;
}
}
package org.onlab.onos.store.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Objects;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.store.Timestamp;
import com.google.common.base.MoreObjects;
......@@ -14,22 +12,20 @@ import com.google.common.collect.ComparisonChain;
// If it is store specific, implement serializable interfaces?
/**
* Default implementation of Timestamp.
* TODO: Better documentation.
*/
public final class OnosTimestamp implements Timestamp {
private final ElementId id;
private final int termNumber;
private final int sequenceNumber;
/**
* Default version tuple.
*
* @param id identifier of the element
* @param termNumber the mastership termNumber
* @param sequenceNumber the sequenceNumber number within the termNumber
*/
public OnosTimestamp(ElementId id, int termNumber, int sequenceNumber) {
this.id = checkNotNull(id);
public OnosTimestamp(int termNumber, int sequenceNumber) {
this.termNumber = termNumber;
this.sequenceNumber = sequenceNumber;
}
......@@ -38,9 +34,6 @@ public final class OnosTimestamp implements Timestamp {
public int compareTo(Timestamp o) {
checkArgument(o instanceof OnosTimestamp, "Must be OnosTimestamp", o);
OnosTimestamp that = (OnosTimestamp) o;
checkArgument(this.id.equals(that.id),
"Cannot compare version for different element this:%s, that:%s",
this, that);
return ComparisonChain.start()
.compare(this.termNumber, that.termNumber)
......@@ -50,7 +43,7 @@ public final class OnosTimestamp implements Timestamp {
@Override
public int hashCode() {
return Objects.hash(id, termNumber, sequenceNumber);
return Objects.hash(termNumber, sequenceNumber);
}
@Override
......@@ -62,30 +55,19 @@ public final class OnosTimestamp implements Timestamp {
return false;
}
OnosTimestamp that = (OnosTimestamp) obj;
return Objects.equals(this.id, that.id) &&
Objects.equals(this.termNumber, that.termNumber) &&
return Objects.equals(this.termNumber, that.termNumber) &&
Objects.equals(this.sequenceNumber, that.sequenceNumber);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("id", id)
.add("termNumber", termNumber)
.add("sequenceNumber", sequenceNumber)
.toString();
}
/**
* Returns the element.
*
* @return element identifier
*/
public ElementId id() {
return id;
}
/**
* Returns the termNumber.
*
* @return termNumber
......@@ -102,4 +84,4 @@ public final class OnosTimestamp implements Timestamp {
public int sequenceNumber() {
return sequenceNumber;
}
}
}
\ No newline at end of file
......
package org.onlab.onos.store.serializers;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.store.impl.OnosTimestamp;
import com.esotericsoftware.kryo.Kryo;
......@@ -20,18 +19,17 @@ public class OnosTimestampSerializer extends Serializer<OnosTimestamp> {
// non-null, immutable
super(false, true);
}
@Override
public void write(Kryo kryo, Output output, OnosTimestamp object) {
kryo.writeClassAndObject(output, object.id());
output.writeInt(object.termNumber());
output.writeInt(object.sequenceNumber());
}
@Override
public OnosTimestamp read(Kryo kryo, Input input, Class<OnosTimestamp> type) {
ElementId id = (ElementId) kryo.readClassAndObject(input);
final int term = input.readInt();
final int sequence = input.readInt();
return new OnosTimestamp(id, term, sequence);
return new OnosTimestamp(term, sequence);
}
}
......