Thomas Vachuska
Committed by Gerrit Code Review

ONOS-3515 Added ability to configure different link-weight functions as defaults…

…; or inject custom ones.

ONOS-3516 Added ability to inject alternate graph path search algorithms.

Change-Id: If5831c198a831ae79a9933fc794eb7deab776e2f
......@@ -25,7 +25,6 @@ package org.onosproject.net;
*/
public final class AnnotationKeys {
// Prohibit instantiation
private AnnotationKeys() {
}
......@@ -81,6 +80,12 @@ public final class AnnotationKeys {
public static final String DURABLE = "durable";
/**
* Annotation key for link metric; used by
* {@link org.onosproject.net.topology.MetricLinkWeight} function.
*/
public static final String METRIC = "metric";
/**
* Annotation key for latency.
*
* @deprecated since Cardinal
......@@ -112,8 +117,14 @@ public final class AnnotationKeys {
*/
public static final String ROUTER_ID = "routerId";
/**
* Annotation key for the static lambda.
*/
public static final String STATIC_LAMBDA = "staticLambda";
/**
* Annotation key for the static port.
*/
public static final String STATIC_PORT = "staticPort";
/**
......
......@@ -15,22 +15,32 @@
*/
package org.onosproject.net.config.basics;
import com.fasterxml.jackson.databind.JsonNode;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import com.fasterxml.jackson.databind.JsonNode;
import java.time.Duration;
import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
/**
* Basic configuration for network infrastructure link.
*/
public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
public static final String TYPE = "type";
public static final String METRIC = "metric";
public static final String LATENCY = "latency";
public static final String BANDWIDTH = "bandwidth";
public static final String IS_DURABLE = "durable";
@Override
public boolean isValid() {
return hasOnlyFields(TYPE, METRIC, LATENCY, BANDWIDTH, IS_DURABLE) &&
isNumber(METRIC, OPTIONAL) && isNumber(LATENCY, OPTIONAL) &&
isNumber(BANDWIDTH, OPTIONAL);
}
/**
* Returns the link type.
*
......@@ -51,6 +61,27 @@ public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
}
/**
* Returns link metric value for use by
* {@link org.onosproject.net.topology.MetricLinkWeight} function.
*
* @return link metric; -1 if not set
*/
public double metric() {
return get(METRIC, -1);
}
/**
* Sets the link metric for use by
* {@link org.onosproject.net.topology.MetricLinkWeight} function.
*
* @param metric new metric; null to clear
* @return self
*/
public BasicLinkConfig metric(Double metric) {
return (BasicLinkConfig) setOrClear(METRIC, metric);
}
/**
* Returns link latency in terms of nanos.
*
* @return link latency; -1 if not set
......
/*
* Copyright 2014-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.net.topology;
import org.onlab.util.GeoLocation;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Annotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import static java.lang.Double.MAX_VALUE;
/**
* Link weight for measuring link cost using the geo distance between link
* vertices as determined by the element longitude/latitude annotation.
*/
public class GeoDistanceLinkWeight implements LinkWeight {
private static final double MAX_KM = 40_075 / 2.0;
private final DeviceService deviceService;
/**
* Creates a new link-weight with access to the specified device service.
*
* @param deviceService device service reference
*/
public GeoDistanceLinkWeight(DeviceService deviceService) {
this.deviceService = deviceService;
}
@Override
public double weight(TopologyEdge edge) {
GeoLocation src = getLocation(edge.link().src().deviceId());
GeoLocation dst = getLocation(edge.link().dst().deviceId());
return src != null && dst != null ? src.kilometersTo(dst) : MAX_KM;
}
private GeoLocation getLocation(DeviceId deviceId) {
Device d = deviceService.getDevice(deviceId);
Annotations a = d != null ? d.annotations() : null;
double latitude = getDouble(a, AnnotationKeys.LATITUDE);
double longitude = getDouble(a, AnnotationKeys.LONGITUDE);
return latitude == MAX_VALUE || longitude == MAX_VALUE ? null :
new GeoLocation(latitude, longitude);
}
private double getDouble(Annotations a, String key) {
String value = a != null ? a.value(key) : null;
try {
return value != null ? Double.parseDouble(value) : MAX_VALUE;
} catch (NumberFormatException e) {
return MAX_VALUE;
}
}
}
package org.onosproject.net.topology;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
/**
* Link weight for measuring link cost as hop count with indirect links
* being as expensive as traversing the entire graph to assume the worst.
*/
public class HopCountLinkWeight implements LinkWeight {
private final int indirectLinkCost;
/**
* Creates a new hop-count weight.
*/
public HopCountLinkWeight() {
this.indirectLinkCost = Short.MAX_VALUE;
}
/**
* Creates a new hop-count weight with the specified cost of indirect links.
*/
public HopCountLinkWeight(int indirectLinkCost) {
this.indirectLinkCost = indirectLinkCost;
}
@Override
public double weight(TopologyEdge edge) {
// To force preference to use direct paths first, make indirect
// links as expensive as the linear vertex traversal.
return edge.link().state() ==
ACTIVE ? (edge.link().type() ==
INDIRECT ? indirectLinkCost : 1) : -1;
}
}
/*
* Copyright 2014-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.net.topology;
import org.onosproject.net.AnnotationKeys;
/**
* Link weight for measuring link cost using the link metric annotation.
*/
public class MetricLinkWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
String v = edge.link().annotations().value(AnnotationKeys.METRIC);
try {
return v != null ? Double.parseDouble(v) : 1;
} catch (NumberFormatException e) {
return 1;
}
}
}
/*
* Copyright 2014-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.net.topology;
import org.onlab.graph.GraphPathSearch;
/**
* Provides administrative abilities to tailor the path service behaviours.
*/
public interface PathAdminService {
/**
* Sets the specified link-weight function to be used as a default.
* If null is specified, the builtin default hop-count link-weight will be
* used.
*
* @param linkWeight default link-weight function
*/
void setDefaultLinkWeight(LinkWeight linkWeight);
/**
* Sets the specified graph path search algorightm to be used as a default.
* If null is specified, the builtin default all-shortest-paths Dijkstra
* algorithm will be used.
*
* @param graphPathSearch default graph path search algorithm
*/
void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch);
}
......@@ -30,8 +30,9 @@ import java.util.Set;
public interface PathService {
/**
* Returns the set of all shortest paths, precomputed in terms of hop-count,
* between the specified source and destination elements.
* Returns the set of all shortest paths between the specified source and
* destination elements. The path is computed using the default edge-weight
* function, which by default is hop-count.
*
* @param src source element
* @param dst destination element
......@@ -40,9 +41,9 @@ public interface PathService {
Set<Path> getPaths(ElementId src, ElementId dst);
/**
* Returns the set of all shortest paths, computed using the supplied
* edge-weight entity, between the specified source and destination
* network elements.
* Returns the set of all shortest paths between the specified source and
* destination network elements. The path is computed using the supplied
* edge-weight function.
*
* @param src source element
* @param dst destination element
......@@ -52,8 +53,9 @@ public interface PathService {
Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight);
/**
* Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
* between the specified source and destination devices.
* Returns the set of all disjoint shortest path pairs between the
* specified source and destination elements. The path is computed using
* the default edge-weight function, which by default is hop-count.
*
* @param src source device
* @param dst destination device
......@@ -62,8 +64,9 @@ public interface PathService {
Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst);
/**
* Returns the set of all disjoint shortest path pairs, computed using the supplied
* edge-weight entity, between the specified source and destination devices.
* Returns the set of all disjoint shortest path pairs between the
* specified source and destination elements. The path is computed using
* the supplied edge-weight function.
*
* @param src source device
* @param dst destination device
......@@ -74,8 +77,10 @@ public interface PathService {
LinkWeight weight);
/**
* Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
* between the specified source and destination devices.
* Returns the set of all disjoint shortest path pairs between the
* specified source and destination elements and taking into consideration
* the provided risk profile. The path is computed using the default
* edge-weight function, which by default is hop-count.
*
* @param src source device
* @param dst destination device
......@@ -86,8 +91,10 @@ public interface PathService {
Map<Link, Object> riskProfile);
/**
* Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
* between the specified source and destination devices.
* Returns the set of all disjoint shortest path pairs between the
* specified source and destination elements and taking into consideration
* the provided risk profile. The path is computed using the supplied
* edge-weight function.
*
* @param src source device
* @param dst destination device
......@@ -96,6 +103,7 @@ public interface PathService {
* @return set of all shortest paths between the two devices
*/
Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
LinkWeight weight, Map<Link, Object> riskProfile);
LinkWeight weight,
Map<Link, Object> riskProfile);
}
......
......@@ -43,12 +43,15 @@ import org.onosproject.net.topology.ClusterId;
import org.onosproject.net.topology.DefaultTopologyCluster;
import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.GraphDescription;
import org.onosproject.net.topology.HopCountLinkWeight;
import org.onosproject.net.topology.LinkWeight;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyCluster;
import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.net.topology.TopologyVertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
......@@ -61,7 +64,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
import static org.onlab.util.Tools.isNullOrEmpty;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.State.INACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
......@@ -71,18 +73,22 @@ import static org.onosproject.net.Link.Type.INDIRECT;
*/
public class DefaultTopology extends AbstractModel implements Topology {
private static final Logger log = LoggerFactory.getLogger(DefaultTopology.class);
private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA = new DijkstraGraphSearch<>();
private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN = new TarjanGraphSearch<>();
private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
new SuurballeGraphSearch<>();
private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE = new SuurballeGraphSearch<>();
private static LinkWeight defaultLinkWeight = null;
private static GraphPathSearch<TopologyVertex, TopologyEdge> defaultGraphPathSearch = null;
private final long time;
private final long creationTime;
private final long computeCost;
private final TopologyGraph graph;
private final LinkWeight weight;
private final LinkWeight hopCountWeight;
private final Supplier<SccResult<TopologyVertex, TopologyEdge>> clusterResults;
private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
......@@ -91,6 +97,30 @@ public class DefaultTopology extends AbstractModel implements Topology {
private final Supplier<ClusterIndexes> clusterIndexes;
/**
* Sets the default link-weight to be used when computing paths. If null is
* specified, the builtin default link-weight measuring hop-counts will be
* used.
*
* @param linkWeight new default link-weight
*/
public static void setDefaultLinkWeight(LinkWeight linkWeight) {
log.info("Setting new default link-weight function to {}", linkWeight);
defaultLinkWeight = linkWeight;
}
/**
* Sets the default lpath search algorighm to be used when computing paths.
* If null is specified, the builtin default Dijkstra will be used.
*
* @param graphPathSearch new default algorithm
*/
public static void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
log.info("Setting new default graph path algorithm to {}", graphPathSearch);
defaultGraphPathSearch = graphPathSearch;
}
/**
* Creates a topology descriptor attributed to the specified provider.
*
* @param providerId identity of the provider
......@@ -113,7 +143,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
this.clusterIndexes = Suppliers.memoize(() -> buildIndexes());
this.weight = new HopCountLinkWeight(graph.getVertexes().size());
this.hopCountWeight = new HopCountLinkWeight(graph.getVertexes().size());
this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
this.computeCost = Math.max(0, System.nanoTime() - time);
......@@ -294,7 +324,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of shortest paths
*/
public Set<Path> getPaths(DeviceId src, DeviceId dst) {
return getPaths(src, dst, null);
return getPaths(src, dst, linkWeight());
}
/**
......@@ -307,8 +337,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of shortest paths
*/
public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Set<TopologyVertex> vertices = graph.getVertexes();
if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
// src or dst not part of the current graph
......@@ -316,7 +346,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
DIJKSTRA.search(graph, srcV, dstV, weight, ALL_PATHS);
graphPathSearch().search(graph, srcV, dstV, weight, ALL_PATHS);
ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
builder.add(networkPath(path));
......@@ -334,7 +364,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of shortest disjoint path pairs
*/
public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) {
return getDisjointPaths(src, dst, (LinkWeight) null);
return getDisjointPaths(src, dst, linkWeight());
}
/**
......@@ -347,8 +377,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of disjoint shortest path pairs
*/
public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Set<TopologyVertex> vertices = graph.getVertexes();
if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
// src or dst not part of the current graph
......@@ -438,7 +468,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of shortest disjoint paths
*/
public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, Map<Link, Object> riskProfile) {
return getDisjointPaths(src, dst, null, riskProfile);
return getDisjointPaths(src, dst, linkWeight(), riskProfile);
}
// Converts graph path to a network path with the same cost.
......@@ -499,8 +529,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Processes a map of broadcast sets for each cluster.
private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap
.builder();
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
for (TopologyCluster cluster : clusters.get().values()) {
addClusterBroadcastSet(cluster, builder);
}
......@@ -512,7 +541,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
// all other devices within the cluster.
private void addClusterBroadcastSet(TopologyCluster cluster, Builder<ClusterId, ConnectPoint> builder) {
// Use the graph root search results to build the broadcast set.
Result<TopologyVertex, TopologyEdge> result = DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
Result<TopologyVertex, TopologyEdge> result = DIJKSTRA.search(graph, cluster.root(), null, hopCountWeight, 1);
for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
TopologyVertex vertex = entry.getKey();
......@@ -577,23 +606,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
linksBuilder.build());
}
// Link weight for measuring link cost as hop count with indirect links
// being as expensive as traversing the entire graph to assume the worst.
private static class HopCountLinkWeight implements LinkWeight {
private final int indirectLinkCost;
HopCountLinkWeight(int indirectLinkCost) {
this.indirectLinkCost = indirectLinkCost;
private GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch() {
return defaultGraphPathSearch != null ? defaultGraphPathSearch : DIJKSTRA;
}
@Override
public double weight(TopologyEdge edge) {
// To force preference to use direct paths first, make indirect
// links as expensive as the linear vertex traversal.
return edge.link().state() ==
ACTIVE ? (edge.link().type() ==
INDIRECT ? indirectLinkCost : 1) : -1;
}
private LinkWeight linkWeight() {
return defaultLinkWeight != null ? defaultLinkWeight : hopCountWeight;
}
// Link weight for preventing traversal over indirect links.
......
......@@ -38,6 +38,7 @@ import org.slf4j.Logger;
public final class BasicLinkOperator implements ConfigOperator {
private static final long DEF_BANDWIDTH = -1L;
private static final double DEF_METRIC = -1;
private static final Duration DEF_DURATION = Duration.ofNanos(-1L);
private static final Logger log = getLogger(BasicLinkOperator.class);
......@@ -77,6 +78,9 @@ public final class BasicLinkOperator implements ConfigOperator {
*/
public static SparseAnnotations combine(BasicLinkConfig cfg, SparseAnnotations an) {
DefaultAnnotations.Builder b = DefaultAnnotations.builder();
if (cfg.metric() != DEF_METRIC) {
b.set(AnnotationKeys.METRIC, String.valueOf(cfg.metric()));
}
if (cfg.latency() != DEF_DURATION) {
b.set(AnnotationKeys.LATENCY, cfg.latency().toString());
}
......
......@@ -15,44 +15,43 @@
*/
package org.onosproject.store.topology.impl;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.util.Tools.isNullOrEmpty;
import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
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.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.graph.GraphPathSearch;
import org.onlab.util.KryoNamespace;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.common.DefaultTopology;
import org.onosproject.event.Event;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.DisjointPath;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.DisjointPath;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.topology.ClusterId;
import org.onosproject.net.topology.DefaultGraphDescription;
import org.onosproject.net.topology.GeoDistanceLinkWeight;
import org.onosproject.net.topology.GraphDescription;
import org.onosproject.net.topology.LinkWeight;
import org.onosproject.net.topology.MetricLinkWeight;
import org.onosproject.net.topology.PathAdminService;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyCluster;
import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyEvent;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.net.topology.TopologyStore;
import org.onosproject.net.topology.TopologyStoreDelegate;
import org.onosproject.net.topology.TopologyVertex;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.EventuallyConsistentMap;
......@@ -60,8 +59,23 @@ import org.onosproject.store.service.EventuallyConsistentMapEvent;
import org.onosproject.store.service.EventuallyConsistentMapListener;
import org.onosproject.store.service.LogicalClockService;
import org.onosproject.store.service.StorageService;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.isNullOrEmpty;
import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of topology snapshots using trivial in-memory
* structures implementation.
......@@ -73,9 +87,12 @@ import org.slf4j.Logger;
@Service
public class DistributedTopologyStore
extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
implements TopologyStore {
implements TopologyStore, PathAdminService {
private final Logger log = getLogger(getClass());
private static final String FORMAT = "Settings: linkWeightFunction={}";
private volatile DefaultTopology current =
new DefaultTopology(ProviderId.NONE,
new DefaultGraphDescription(0L, System.currentTimeMillis(),
......@@ -91,6 +108,21 @@ public class DistributedTopologyStore
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
private static final String HOP_COUNT = "hopCount";
private static final String LINK_METRIC = "linkMetric";
private static final String GEO_DISTANCE = "geoDistance";
private static final String DEFAULT_LINK_WEIGHT_FUNCTION = "hopCount";
@Property(name = "linkWeightFunction", value = DEFAULT_LINK_WEIGHT_FUNCTION,
label = "Default link-weight function: hopCount, linkMetric, geoDistance")
private String linkWeightFunction = DEFAULT_LINK_WEIGHT_FUNCTION;
// Cluster root to broadcast points bindings to allow convergence to
// a shared broadcast tree; node that is the master of the cluster root
// is the primary.
......@@ -100,7 +132,8 @@ public class DistributedTopologyStore
new InternalBroadcastPointListener();
@Activate
public void activate() {
protected void activate() {
configService.registerProperties(getClass());
KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API);
......@@ -114,12 +147,30 @@ public class DistributedTopologyStore
}
@Deactivate
public void deactivate() {
protected void deactivate() {
configService.unregisterProperties(getClass(), false);
broadcastPoints.removeListener(listener);
broadcastPoints.destroy();
log.info("Stopped");
}
@Modified
protected void modified(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
String newLinkWeightFunction = get(properties, "linkWeightFunction");
if (newLinkWeightFunction != null &&
!Objects.equals(newLinkWeightFunction, linkWeightFunction)) {
linkWeightFunction = newLinkWeightFunction;
LinkWeight weight = linkWeightFunction.equals(LINK_METRIC) ?
new MetricLinkWeight() :
linkWeightFunction.equals(GEO_DISTANCE) ?
new GeoDistanceLinkWeight(deviceService) : null;
setDefaultLinkWeight(weight);
}
log.info(FORMAT, linkWeightFunction);
}
@Override
public Topology currentTopology() {
return current;
......@@ -263,6 +314,16 @@ public class DistributedTopologyStore
return (DefaultTopology) topology;
}
@Override
public void setDefaultLinkWeight(LinkWeight linkWeight) {
DefaultTopology.setDefaultLinkWeight(linkWeight);
}
@Override
public void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
DefaultTopology.setDefaultGraphPathSearch(graphPathSearch);
}
private class InternalBroadcastPointListener
implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
@Override
......
......@@ -168,7 +168,7 @@ public class NullProviders {
@Activate
public void activate(ComponentContext context) {
public void activate() {
cfgService.registerProperties(getClass());
deviceProviderService = deviceProviderRegistry.register(deviceProvider);
......@@ -180,7 +180,7 @@ public class NullProviders {
}
@Deactivate
public void deactivate(ComponentContext context) {
public void deactivate() {
cfgService.unregisterProperties(getClass(), false);
tearDown();
......
/*
* Copyright 2014-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.onlab.util;
import com.google.common.base.MoreObjects;
/**
* Geo location specified in terms of longitude and latitude.
*/
public class GeoLocation {
public static final double EARTH_RADIUS_KM = 6378.1370D;
private final double latitude;
private final double longitude;
/**
* Creates a new location using the specified coordinates.
*
* @param latitude latitude line
* @param longitude longitude line
*/
public GeoLocation(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
/**
* Returns the latitude of this location.
*
* @return latitude
*/
public double latitude() {
return latitude;
}
/**
* Returns the longitude of this location.
*
* @return longitude
*/
public double longitude() {
return longitude;
}
/**
* Returns the distance in kilometers, between this location and another.
*
* @param other other geo location
* @return distance in kilometers
*/
public double kilometersTo(GeoLocation other) {
double hereLat = Math.toRadians(latitude);
double hereLon = Math.toRadians(longitude);
double thereLat = Math.toRadians(other.latitude);
double thereLon = Math.toRadians(other.longitude);
double cos = Math.sin(hereLat) * Math.sin(thereLat) +
Math.cos(hereLat) * Math.cos(thereLat) * Math.cos(hereLon - thereLon);
return Math.acos(cos) * EARTH_RADIUS_KM;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("latitude", latitude)
.add("longitude", longitude)
.toString();
}
}
/*
* Copyright 2014-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.onlab.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test suite of the geo location.
*/
public class GeoLocationTest {
@Test
public void basics() {
GeoLocation nLoc = new GeoLocation(40.7127, -74.0059);
GeoLocation wLoc = new GeoLocation(38.9047, -77.0164);
assertEquals("incorrect latitude", 40.7127, nLoc.latitude(), 0.0001);
assertEquals("incorrect longitude", -74.00598, nLoc.longitude(), 0.0001);
assertEquals("incorrect distance", 326.74, nLoc.kilometersTo(wLoc), 0.01);
}
}
\ No newline at end of file