blob: c59d30ddbdf2e27ab8b5936209648ef0988ade1f [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.topology.impl;
alshabib339a3d92014-09-26 17:54:32 -070017
alshabib339a3d92014-09-26 17:54:32 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib339a3d92014-09-26 17:54:32 -070025import org.apache.felix.scr.annotations.Service;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080026import org.onlab.graph.GraphPathSearch;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070027import org.onlab.util.KryoNamespace;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080028import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070029import org.onosproject.common.DefaultTopology;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.event.Event;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070031import org.onosproject.mastership.MastershipService;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.DeviceId;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080034import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.Link;
36import org.onosproject.net.Path;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080037import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.provider.ProviderId;
39import org.onosproject.net.topology.ClusterId;
40import org.onosproject.net.topology.DefaultGraphDescription;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080041import org.onosproject.net.topology.GeoDistanceLinkWeight;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.topology.GraphDescription;
Andrey Komarov2398d962016-09-26 15:11:23 +030043import org.onosproject.net.topology.LinkWeigher;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.topology.LinkWeight;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080045import org.onosproject.net.topology.MetricLinkWeight;
46import org.onosproject.net.topology.PathAdminService;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.topology.Topology;
48import org.onosproject.net.topology.TopologyCluster;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080049import org.onosproject.net.topology.TopologyEdge;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.topology.TopologyEvent;
51import org.onosproject.net.topology.TopologyGraph;
52import org.onosproject.net.topology.TopologyStore;
53import org.onosproject.net.topology.TopologyStoreDelegate;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080054import org.onosproject.net.topology.TopologyVertex;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.store.AbstractStore;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070056import org.onosproject.store.serializers.KryoNamespaces;
57import org.onosproject.store.service.EventuallyConsistentMap;
58import org.onosproject.store.service.EventuallyConsistentMapEvent;
59import org.onosproject.store.service.EventuallyConsistentMapListener;
60import org.onosproject.store.service.LogicalClockService;
61import org.onosproject.store.service.StorageService;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080062import org.osgi.service.component.ComponentContext;
alshabib339a3d92014-09-26 17:54:32 -070063import org.slf4j.Logger;
64
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080065import java.util.Collections;
66import java.util.Dictionary;
67import java.util.List;
68import java.util.Map;
69import java.util.Objects;
70import java.util.Set;
71import java.util.stream.Collectors;
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070072import java.util.stream.Stream;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080073
74import static com.google.common.base.Preconditions.checkArgument;
75import static org.onlab.util.Tools.get;
76import static org.onlab.util.Tools.isNullOrEmpty;
Andrey Komarov2398d962016-09-26 15:11:23 +030077import static org.onosproject.net.topology.AdapterLinkWeigher.adapt;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080078import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
79import static org.slf4j.LoggerFactory.getLogger;
80
alshabib339a3d92014-09-26 17:54:32 -070081/**
82 * Manages inventory of topology snapshots using trivial in-memory
83 * structures implementation.
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070084 * <p>
Brian O'Connor44008532014-12-04 16:41:36 -080085 * Note: This component is not distributed per-se. It runs on every
86 * instance and feeds off of other distributed stores.
alshabib339a3d92014-09-26 17:54:32 -070087 */
alshabib339a3d92014-09-26 17:54:32 -070088@Component(immediate = true)
89@Service
90public class DistributedTopologyStore
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070091 extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080092 implements TopologyStore, PathAdminService {
alshabib339a3d92014-09-26 17:54:32 -070093
94 private final Logger log = getLogger(getClass());
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080095
96 private static final String FORMAT = "Settings: linkWeightFunction={}";
97
Jonathan Hart29858af2014-10-29 15:33:18 -070098 private volatile DefaultTopology current =
99 new DefaultTopology(ProviderId.NONE,
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700100 new DefaultGraphDescription(0L, System.currentTimeMillis(),
Sho SHIMIZU21d00692016-08-15 11:15:28 -0700101 Collections.emptyList(),
102 Collections.emptyList()));
alshabib339a3d92014-09-26 17:54:32 -0700103
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected StorageService storageService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected LogicalClockService clockService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected MastershipService mastershipService;
112
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected ComponentConfigService configService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected DeviceService deviceService;
118
119 private static final String HOP_COUNT = "hopCount";
120 private static final String LINK_METRIC = "linkMetric";
121 private static final String GEO_DISTANCE = "geoDistance";
122
123 private static final String DEFAULT_LINK_WEIGHT_FUNCTION = "hopCount";
124 @Property(name = "linkWeightFunction", value = DEFAULT_LINK_WEIGHT_FUNCTION,
125 label = "Default link-weight function: hopCount, linkMetric, geoDistance")
126 private String linkWeightFunction = DEFAULT_LINK_WEIGHT_FUNCTION;
127
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700128 // Cluster root to broadcast points bindings to allow convergence to
129 // a shared broadcast tree; node that is the master of the cluster root
130 // is the primary.
131 private EventuallyConsistentMap<DeviceId, Set<ConnectPoint>> broadcastPoints;
132
133 private EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> listener =
134 new InternalBroadcastPointListener();
135
alshabib339a3d92014-09-26 17:54:32 -0700136 @Activate
sisubram1a100a92017-08-09 10:26:00 +0000137 protected void activate(ComponentContext context) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800138 configService.registerProperties(getClass());
sisubram1a100a92017-08-09 10:26:00 +0000139 modified(context);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700140 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
141 .register(KryoNamespaces.API);
142
143 broadcastPoints = storageService.<DeviceId, Set<ConnectPoint>>eventuallyConsistentMapBuilder()
144 .withName("onos-broadcast-trees")
145 .withSerializer(hostSerializer)
146 .withTimestampProvider((k, v) -> clockService.getTimestamp())
147 .build();
148 broadcastPoints.addListener(listener);
alshabib339a3d92014-09-26 17:54:32 -0700149 log.info("Started");
150 }
151
152 @Deactivate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800153 protected void deactivate() {
154 configService.unregisterProperties(getClass(), false);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700155 broadcastPoints.removeListener(listener);
156 broadcastPoints.destroy();
alshabib339a3d92014-09-26 17:54:32 -0700157 log.info("Stopped");
158 }
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700159
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800160 @Modified
161 protected void modified(ComponentContext context) {
162 Dictionary<?, ?> properties = context.getProperties();
163
164 String newLinkWeightFunction = get(properties, "linkWeightFunction");
165 if (newLinkWeightFunction != null &&
166 !Objects.equals(newLinkWeightFunction, linkWeightFunction)) {
167 linkWeightFunction = newLinkWeightFunction;
168 LinkWeight weight = linkWeightFunction.equals(LINK_METRIC) ?
169 new MetricLinkWeight() :
170 linkWeightFunction.equals(GEO_DISTANCE) ?
171 new GeoDistanceLinkWeight(deviceService) : null;
172 setDefaultLinkWeight(weight);
173 }
174 log.info(FORMAT, linkWeightFunction);
175 }
176
alshabib339a3d92014-09-26 17:54:32 -0700177 @Override
178 public Topology currentTopology() {
179 return current;
180 }
181
182 @Override
183 public boolean isLatest(Topology topology) {
184 // Topology is current only if it is the same as our current topology
185 return topology == current;
186 }
187
188 @Override
189 public TopologyGraph getGraph(Topology topology) {
190 return defaultTopology(topology).getGraph();
191 }
192
193 @Override
194 public Set<TopologyCluster> getClusters(Topology topology) {
195 return defaultTopology(topology).getClusters();
196 }
197
198 @Override
199 public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
200 return defaultTopology(topology).getCluster(clusterId);
201 }
202
203 @Override
204 public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
205 return defaultTopology(topology).getClusterDevices(cluster);
206 }
207
208 @Override
209 public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
210 return defaultTopology(topology).getClusterLinks(cluster);
211 }
212
213 @Override
214 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
215 return defaultTopology(topology).getPaths(src, dst);
216 }
217
218 @Override
219 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700220 LinkWeight weight) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300221 return getPaths(topology, src, dst, adapt(weight));
222 }
223
224 @Override
225 public Set<Path> getPaths(Topology topology, DeviceId src,
226 DeviceId dst, LinkWeigher weigher) {
227 return defaultTopology(topology).getPaths(src, dst, weigher);
alshabib339a3d92014-09-26 17:54:32 -0700228 }
229
230 @Override
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700231 public Set<Path> getKShortestPaths(Topology topology,
232 DeviceId src, DeviceId dst,
233 LinkWeigher weigher,
234 int maxPaths) {
235 return defaultTopology(topology).getKShortestPaths(src, dst, weigher, maxPaths);
236 }
237
238 @Override
239 public Stream<Path> getKShortestPaths(Topology topology,
240 DeviceId src,
241 DeviceId dst,
242 LinkWeigher weigher) {
243 return defaultTopology(topology).getKShortestPaths(src, dst, weigher);
244 }
245
246 @Override
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700247 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
248 return defaultTopology(topology).getDisjointPaths(src, dst);
249 }
250
251 @Override
252 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
253 LinkWeight weight) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300254 return getDisjointPaths(topology, src, dst, adapt(weight));
255 }
256
257 @Override
258 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
259 DeviceId dst, LinkWeigher weigher) {
260 return defaultTopology(topology).getDisjointPaths(src, dst, weigher);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700261 }
262
263 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700264 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
265 Map<Link, Object> riskProfile) {
266 return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700267 }
268
269 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700270 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
271 LinkWeight weight, Map<Link, Object> riskProfile) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300272 return getDisjointPaths(topology, src, dst, adapt(weight), riskProfile);
273 }
274
275 @Override
276 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
277 DeviceId dst, LinkWeigher weigher,
278 Map<Link, Object> riskProfile) {
279 return defaultTopology(topology).getDisjointPaths(src, dst, weigher, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700280 }
281
282 @Override
alshabib339a3d92014-09-26 17:54:32 -0700283 public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
284 return defaultTopology(topology).isInfrastructure(connectPoint);
285 }
286
287 @Override
288 public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
289 return defaultTopology(topology).isBroadcastPoint(connectPoint);
290 }
291
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700292 private boolean isBroadcastPoint(ConnectPoint connectPoint) {
293 // Any non-infrastructure, i.e. edge points are assumed to be OK.
294 if (!current.isInfrastructure(connectPoint)) {
295 return true;
296 }
297
298 // Find the cluster to which the device belongs.
299 TopologyCluster cluster = current.getCluster(connectPoint.deviceId());
300 checkArgument(cluster != null, "No cluster found for device %s", connectPoint.deviceId());
301
302 // If the broadcast set is null or empty, or if the point explicitly
303 // belongs to it, return true;
304 Set<ConnectPoint> points = broadcastPoints.get(cluster.root().deviceId());
305 return isNullOrEmpty(points) || points.contains(connectPoint);
306 }
307
alshabib339a3d92014-09-26 17:54:32 -0700308 @Override
309 public TopologyEvent updateTopology(ProviderId providerId,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700310 GraphDescription graphDescription,
311 List<Event> reasons) {
alshabib339a3d92014-09-26 17:54:32 -0700312 // Have the default topology construct self from the description data.
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700313 DefaultTopology newTopology =
314 new DefaultTopology(providerId, graphDescription, this::isBroadcastPoint);
315 updateBroadcastPoints(newTopology);
alshabib339a3d92014-09-26 17:54:32 -0700316
317 // Promote the new topology to current and return a ready-to-send event.
318 synchronized (this) {
You Wang66b77fa2017-02-06 16:51:10 -0800319 // Make sure that what we're given is indeed newer than what we
320 // already have.
321 if (current != null && newTopology.time() < current.time()) {
322 return null;
323 }
alshabib339a3d92014-09-26 17:54:32 -0700324 current = newTopology;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700325 return new TopologyEvent(TOPOLOGY_CHANGED, current, reasons);
alshabib339a3d92014-09-26 17:54:32 -0700326 }
327 }
328
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700329 private void updateBroadcastPoints(DefaultTopology topology) {
330 // Remove any broadcast trees rooted by devices for which we are master.
331 Set<DeviceId> toRemove = broadcastPoints.keySet().stream()
332 .filter(mastershipService::isLocalMaster)
333 .collect(Collectors.toSet());
334
335 // Update the broadcast trees rooted by devices for which we are master.
336 topology.getClusters().forEach(c -> {
337 toRemove.remove(c.root().deviceId());
338 if (mastershipService.isLocalMaster(c.root().deviceId())) {
339 broadcastPoints.put(c.root().deviceId(),
340 topology.broadcastPoints(c.id()));
341 }
342 });
343
344 toRemove.forEach(broadcastPoints::remove);
345 }
346
alshabib339a3d92014-09-26 17:54:32 -0700347 // Validates the specified topology and returns it as a default
348 private DefaultTopology defaultTopology(Topology topology) {
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700349 checkArgument(topology instanceof DefaultTopology,
350 "Topology class %s not supported", topology.getClass());
351 return (DefaultTopology) topology;
alshabib339a3d92014-09-26 17:54:32 -0700352 }
353
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800354 @Override
355 public void setDefaultLinkWeight(LinkWeight linkWeight) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300356 DefaultTopology.setDefaultLinkWeigher(adapt(linkWeight));
357 }
358
359 @Override
360 public void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
361 DefaultTopology.setDefaultLinkWeigher(linkWeigher);
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800362 }
363
364 @Override
365 public void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
366 DefaultTopology.setDefaultGraphPathSearch(graphPathSearch);
367 }
368
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700369 private class InternalBroadcastPointListener
370 implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
371 @Override
372 public void event(EventuallyConsistentMapEvent<DeviceId, Set<ConnectPoint>> event) {
373 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
374 if (!event.value().isEmpty()) {
Madan Jampanib6e884b2016-06-23 01:34:15 -0700375 log.debug("Cluster rooted at {} has {} broadcast-points; #{}",
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700376 event.key(), event.value().size(), event.value().hashCode());
377 }
378 }
379 }
380 }
alshabib339a3d92014-09-26 17:54:32 -0700381}