blob: 04c9961c9e8cc1eec78000a523066908e14cf149 [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
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080018import org.onlab.graph.GraphPathSearch;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070019import org.onlab.util.KryoNamespace;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080020import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070021import org.onosproject.common.DefaultTopology;
Brian O'Connorabafb502014-12-02 22:26:20 -080022import org.onosproject.event.Event;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070023import org.onosproject.mastership.MastershipService;
Brian O'Connorabafb502014-12-02 22:26:20 -080024import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.net.DeviceId;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080026import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.net.Link;
28import org.onosproject.net.Path;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080029import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.provider.ProviderId;
31import org.onosproject.net.topology.ClusterId;
32import org.onosproject.net.topology.DefaultGraphDescription;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080033import org.onosproject.net.topology.GeoDistanceLinkWeight;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.topology.GraphDescription;
Andrey Komarov2398d962016-09-26 15:11:23 +030035import org.onosproject.net.topology.LinkWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080036import org.onosproject.net.topology.MetricLinkWeight;
37import org.onosproject.net.topology.PathAdminService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.topology.Topology;
39import org.onosproject.net.topology.TopologyCluster;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080040import org.onosproject.net.topology.TopologyEdge;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.topology.TopologyEvent;
42import org.onosproject.net.topology.TopologyGraph;
43import org.onosproject.net.topology.TopologyStore;
44import org.onosproject.net.topology.TopologyStoreDelegate;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080045import org.onosproject.net.topology.TopologyVertex;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.store.AbstractStore;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070047import org.onosproject.store.serializers.KryoNamespaces;
48import org.onosproject.store.service.EventuallyConsistentMap;
49import org.onosproject.store.service.EventuallyConsistentMapEvent;
50import org.onosproject.store.service.EventuallyConsistentMapListener;
51import org.onosproject.store.service.LogicalClockService;
52import org.onosproject.store.service.StorageService;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080053import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070054import org.osgi.service.component.annotations.Activate;
55import org.osgi.service.component.annotations.Component;
56import org.osgi.service.component.annotations.Deactivate;
57import org.osgi.service.component.annotations.Modified;
58import org.osgi.service.component.annotations.Reference;
59import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib339a3d92014-09-26 17:54:32 -070060import org.slf4j.Logger;
61
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080062import java.util.Collections;
63import java.util.Dictionary;
64import java.util.List;
65import java.util.Map;
66import java.util.Objects;
67import java.util.Set;
68import java.util.stream.Collectors;
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070069import java.util.stream.Stream;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080070
71import static com.google.common.base.Preconditions.checkArgument;
72import static org.onlab.util.Tools.get;
73import static org.onlab.util.Tools.isNullOrEmpty;
74import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
75import static org.slf4j.LoggerFactory.getLogger;
76
alshabib339a3d92014-09-26 17:54:32 -070077/**
78 * Manages inventory of topology snapshots using trivial in-memory
79 * structures implementation.
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070080 * <p>
Brian O'Connor44008532014-12-04 16:41:36 -080081 * Note: This component is not distributed per-se. It runs on every
82 * instance and feeds off of other distributed stores.
alshabib339a3d92014-09-26 17:54:32 -070083 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084@Component(immediate = true, service = { TopologyStore.class, PathAdminService.class })
alshabib339a3d92014-09-26 17:54:32 -070085public class DistributedTopologyStore
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070086 extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080087 implements TopologyStore, PathAdminService {
alshabib339a3d92014-09-26 17:54:32 -070088
89 private final Logger log = getLogger(getClass());
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080090
91 private static final String FORMAT = "Settings: linkWeightFunction={}";
92
Jonathan Hart29858af2014-10-29 15:33:18 -070093 private volatile DefaultTopology current =
94 new DefaultTopology(ProviderId.NONE,
Thomas Vachuska320c58f2015-08-05 10:42:32 -070095 new DefaultGraphDescription(0L, System.currentTimeMillis(),
Sho SHIMIZU21d00692016-08-15 11:15:28 -070096 Collections.emptyList(),
97 Collections.emptyList()));
alshabib339a3d92014-09-26 17:54:32 -070098
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700100 protected StorageService storageService;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700103 protected LogicalClockService clockService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700106 protected MastershipService mastershipService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800109 protected ComponentConfigService configService;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800112 protected DeviceService deviceService;
113
114 private static final String HOP_COUNT = "hopCount";
115 private static final String LINK_METRIC = "linkMetric";
116 private static final String GEO_DISTANCE = "geoDistance";
117
118 private static final String DEFAULT_LINK_WEIGHT_FUNCTION = "hopCount";
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 //@Property(name = "linkWeightFunction", value = DEFAULT_LINK_WEIGHT_FUNCTION,
120 // label = "Default link-weight function: hopCount, linkMetric, geoDistance")
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800121 private String linkWeightFunction = DEFAULT_LINK_WEIGHT_FUNCTION;
122
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700123 // Cluster root to broadcast points bindings to allow convergence to
124 // a shared broadcast tree; node that is the master of the cluster root
125 // is the primary.
126 private EventuallyConsistentMap<DeviceId, Set<ConnectPoint>> broadcastPoints;
127
128 private EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> listener =
129 new InternalBroadcastPointListener();
130
alshabib339a3d92014-09-26 17:54:32 -0700131 @Activate
sisubram1a100a92017-08-09 10:26:00 +0000132 protected void activate(ComponentContext context) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800133 configService.registerProperties(getClass());
sisubram1a100a92017-08-09 10:26:00 +0000134 modified(context);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700135 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
136 .register(KryoNamespaces.API);
137
138 broadcastPoints = storageService.<DeviceId, Set<ConnectPoint>>eventuallyConsistentMapBuilder()
139 .withName("onos-broadcast-trees")
140 .withSerializer(hostSerializer)
141 .withTimestampProvider((k, v) -> clockService.getTimestamp())
142 .build();
143 broadcastPoints.addListener(listener);
alshabib339a3d92014-09-26 17:54:32 -0700144 log.info("Started");
145 }
146
147 @Deactivate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800148 protected void deactivate() {
149 configService.unregisterProperties(getClass(), false);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700150 broadcastPoints.removeListener(listener);
151 broadcastPoints.destroy();
alshabib339a3d92014-09-26 17:54:32 -0700152 log.info("Stopped");
153 }
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700154
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800155 @Modified
156 protected void modified(ComponentContext context) {
157 Dictionary<?, ?> properties = context.getProperties();
158
159 String newLinkWeightFunction = get(properties, "linkWeightFunction");
160 if (newLinkWeightFunction != null &&
161 !Objects.equals(newLinkWeightFunction, linkWeightFunction)) {
162 linkWeightFunction = newLinkWeightFunction;
Ray Milkey7483e1b2018-02-07 15:43:01 -0800163 LinkWeigher weight = linkWeightFunction.equals(LINK_METRIC) ?
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800164 new MetricLinkWeight() :
165 linkWeightFunction.equals(GEO_DISTANCE) ?
166 new GeoDistanceLinkWeight(deviceService) : null;
Ray Milkey7483e1b2018-02-07 15:43:01 -0800167 setDefaultLinkWeigher(weight);
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800168 }
169 log.info(FORMAT, linkWeightFunction);
170 }
171
alshabib339a3d92014-09-26 17:54:32 -0700172 @Override
173 public Topology currentTopology() {
174 return current;
175 }
176
177 @Override
178 public boolean isLatest(Topology topology) {
179 // Topology is current only if it is the same as our current topology
180 return topology == current;
181 }
182
183 @Override
184 public TopologyGraph getGraph(Topology topology) {
185 return defaultTopology(topology).getGraph();
186 }
187
188 @Override
189 public Set<TopologyCluster> getClusters(Topology topology) {
190 return defaultTopology(topology).getClusters();
191 }
192
193 @Override
194 public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
195 return defaultTopology(topology).getCluster(clusterId);
196 }
197
198 @Override
199 public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
200 return defaultTopology(topology).getClusterDevices(cluster);
201 }
202
203 @Override
204 public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
205 return defaultTopology(topology).getClusterLinks(cluster);
206 }
207
208 @Override
209 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
210 return defaultTopology(topology).getPaths(src, dst);
211 }
212
Andrey Komarov2398d962016-09-26 15:11:23 +0300213
214 @Override
215 public Set<Path> getPaths(Topology topology, DeviceId src,
216 DeviceId dst, LinkWeigher weigher) {
217 return defaultTopology(topology).getPaths(src, dst, weigher);
alshabib339a3d92014-09-26 17:54:32 -0700218 }
219
220 @Override
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700221 public Set<Path> getKShortestPaths(Topology topology,
222 DeviceId src, DeviceId dst,
223 LinkWeigher weigher,
224 int maxPaths) {
225 return defaultTopology(topology).getKShortestPaths(src, dst, weigher, maxPaths);
226 }
227
228 @Override
229 public Stream<Path> getKShortestPaths(Topology topology,
230 DeviceId src,
231 DeviceId dst,
232 LinkWeigher weigher) {
233 return defaultTopology(topology).getKShortestPaths(src, dst, weigher);
234 }
235
236 @Override
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700237 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
238 return defaultTopology(topology).getDisjointPaths(src, dst);
239 }
240
241 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300242 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
243 DeviceId dst, LinkWeigher weigher) {
244 return defaultTopology(topology).getDisjointPaths(src, dst, weigher);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700245 }
246
247 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700248 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
249 Map<Link, Object> riskProfile) {
250 return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700251 }
252
253 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300254 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
255 DeviceId dst, LinkWeigher weigher,
256 Map<Link, Object> riskProfile) {
257 return defaultTopology(topology).getDisjointPaths(src, dst, weigher, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700258 }
259
260 @Override
alshabib339a3d92014-09-26 17:54:32 -0700261 public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
262 return defaultTopology(topology).isInfrastructure(connectPoint);
263 }
264
265 @Override
266 public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
267 return defaultTopology(topology).isBroadcastPoint(connectPoint);
268 }
269
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700270 private boolean isBroadcastPoint(ConnectPoint connectPoint) {
271 // Any non-infrastructure, i.e. edge points are assumed to be OK.
272 if (!current.isInfrastructure(connectPoint)) {
273 return true;
274 }
275
276 // Find the cluster to which the device belongs.
277 TopologyCluster cluster = current.getCluster(connectPoint.deviceId());
278 checkArgument(cluster != null, "No cluster found for device %s", connectPoint.deviceId());
279
280 // If the broadcast set is null or empty, or if the point explicitly
281 // belongs to it, return true;
282 Set<ConnectPoint> points = broadcastPoints.get(cluster.root().deviceId());
283 return isNullOrEmpty(points) || points.contains(connectPoint);
284 }
285
alshabib339a3d92014-09-26 17:54:32 -0700286 @Override
287 public TopologyEvent updateTopology(ProviderId providerId,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700288 GraphDescription graphDescription,
289 List<Event> reasons) {
alshabib339a3d92014-09-26 17:54:32 -0700290 // Have the default topology construct self from the description data.
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700291 DefaultTopology newTopology =
292 new DefaultTopology(providerId, graphDescription, this::isBroadcastPoint);
293 updateBroadcastPoints(newTopology);
alshabib339a3d92014-09-26 17:54:32 -0700294
295 // Promote the new topology to current and return a ready-to-send event.
296 synchronized (this) {
You Wang66b77fa2017-02-06 16:51:10 -0800297 // Make sure that what we're given is indeed newer than what we
298 // already have.
299 if (current != null && newTopology.time() < current.time()) {
300 return null;
301 }
alshabib339a3d92014-09-26 17:54:32 -0700302 current = newTopology;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700303 return new TopologyEvent(TOPOLOGY_CHANGED, current, reasons);
alshabib339a3d92014-09-26 17:54:32 -0700304 }
305 }
306
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700307 private void updateBroadcastPoints(DefaultTopology topology) {
308 // Remove any broadcast trees rooted by devices for which we are master.
309 Set<DeviceId> toRemove = broadcastPoints.keySet().stream()
310 .filter(mastershipService::isLocalMaster)
311 .collect(Collectors.toSet());
312
313 // Update the broadcast trees rooted by devices for which we are master.
314 topology.getClusters().forEach(c -> {
315 toRemove.remove(c.root().deviceId());
316 if (mastershipService.isLocalMaster(c.root().deviceId())) {
317 broadcastPoints.put(c.root().deviceId(),
318 topology.broadcastPoints(c.id()));
319 }
320 });
321
322 toRemove.forEach(broadcastPoints::remove);
323 }
324
alshabib339a3d92014-09-26 17:54:32 -0700325 // Validates the specified topology and returns it as a default
326 private DefaultTopology defaultTopology(Topology topology) {
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700327 checkArgument(topology instanceof DefaultTopology,
328 "Topology class %s not supported", topology.getClass());
329 return (DefaultTopology) topology;
alshabib339a3d92014-09-26 17:54:32 -0700330 }
331
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800332 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300333 public void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
334 DefaultTopology.setDefaultLinkWeigher(linkWeigher);
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800335 }
336
337 @Override
338 public void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
339 DefaultTopology.setDefaultGraphPathSearch(graphPathSearch);
340 }
341
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700342 private class InternalBroadcastPointListener
343 implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
344 @Override
345 public void event(EventuallyConsistentMapEvent<DeviceId, Set<ConnectPoint>> event) {
346 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
347 if (!event.value().isEmpty()) {
Madan Jampanib6e884b2016-06-23 01:34:15 -0700348 log.debug("Cluster rooted at {} has {} broadcast-points; #{}",
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700349 event.key(), event.value().size(), event.value().hashCode());
350 }
351 }
352 }
353 }
alshabib339a3d92014-09-26 17:54:32 -0700354}