blob: 2641635dec90d8f986abdd20a92d561764ef1573 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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;
33import org.onosproject.net.Device;
34import org.onosproject.net.DeviceId;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080035import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.Link;
37import org.onosproject.net.Path;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080038import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.provider.ProviderId;
40import org.onosproject.net.topology.ClusterId;
41import org.onosproject.net.topology.DefaultGraphDescription;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080042import org.onosproject.net.topology.GeoDistanceLinkWeight;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.topology.GraphDescription;
44import 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;
72
73import static com.google.common.base.Preconditions.checkArgument;
74import static org.onlab.util.Tools.get;
75import static org.onlab.util.Tools.isNullOrEmpty;
76import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
77import static org.slf4j.LoggerFactory.getLogger;
78
alshabib339a3d92014-09-26 17:54:32 -070079/**
80 * Manages inventory of topology snapshots using trivial in-memory
81 * structures implementation.
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070082 * <p>
Brian O'Connor44008532014-12-04 16:41:36 -080083 * Note: This component is not distributed per-se. It runs on every
84 * instance and feeds off of other distributed stores.
alshabib339a3d92014-09-26 17:54:32 -070085 */
alshabib339a3d92014-09-26 17:54:32 -070086@Component(immediate = true)
87@Service
88public class DistributedTopologyStore
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070089 extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080090 implements TopologyStore, PathAdminService {
alshabib339a3d92014-09-26 17:54:32 -070091
92 private final Logger log = getLogger(getClass());
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080093
94 private static final String FORMAT = "Settings: linkWeightFunction={}";
95
Jonathan Hart29858af2014-10-29 15:33:18 -070096 private volatile DefaultTopology current =
97 new DefaultTopology(ProviderId.NONE,
Thomas Vachuska320c58f2015-08-05 10:42:32 -070098 new DefaultGraphDescription(0L, System.currentTimeMillis(),
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070099 Collections.<Device>emptyList(),
100 Collections.<Link>emptyList()));
alshabib339a3d92014-09-26 17:54:32 -0700101
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected StorageService storageService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected LogicalClockService clockService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected MastershipService mastershipService;
110
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected ComponentConfigService configService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected DeviceService deviceService;
116
117 private static final String HOP_COUNT = "hopCount";
118 private static final String LINK_METRIC = "linkMetric";
119 private static final String GEO_DISTANCE = "geoDistance";
120
121 private static final String DEFAULT_LINK_WEIGHT_FUNCTION = "hopCount";
122 @Property(name = "linkWeightFunction", value = DEFAULT_LINK_WEIGHT_FUNCTION,
123 label = "Default link-weight function: hopCount, linkMetric, geoDistance")
124 private String linkWeightFunction = DEFAULT_LINK_WEIGHT_FUNCTION;
125
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700126 // Cluster root to broadcast points bindings to allow convergence to
127 // a shared broadcast tree; node that is the master of the cluster root
128 // is the primary.
129 private EventuallyConsistentMap<DeviceId, Set<ConnectPoint>> broadcastPoints;
130
131 private EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> listener =
132 new InternalBroadcastPointListener();
133
alshabib339a3d92014-09-26 17:54:32 -0700134 @Activate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800135 protected void activate() {
136 configService.registerProperties(getClass());
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700137 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
138 .register(KryoNamespaces.API);
139
140 broadcastPoints = storageService.<DeviceId, Set<ConnectPoint>>eventuallyConsistentMapBuilder()
141 .withName("onos-broadcast-trees")
142 .withSerializer(hostSerializer)
143 .withTimestampProvider((k, v) -> clockService.getTimestamp())
144 .build();
145 broadcastPoints.addListener(listener);
alshabib339a3d92014-09-26 17:54:32 -0700146 log.info("Started");
147 }
148
149 @Deactivate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800150 protected void deactivate() {
151 configService.unregisterProperties(getClass(), false);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700152 broadcastPoints.removeListener(listener);
153 broadcastPoints.destroy();
alshabib339a3d92014-09-26 17:54:32 -0700154 log.info("Stopped");
155 }
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700156
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800157 @Modified
158 protected void modified(ComponentContext context) {
159 Dictionary<?, ?> properties = context.getProperties();
160
161 String newLinkWeightFunction = get(properties, "linkWeightFunction");
162 if (newLinkWeightFunction != null &&
163 !Objects.equals(newLinkWeightFunction, linkWeightFunction)) {
164 linkWeightFunction = newLinkWeightFunction;
165 LinkWeight weight = linkWeightFunction.equals(LINK_METRIC) ?
166 new MetricLinkWeight() :
167 linkWeightFunction.equals(GEO_DISTANCE) ?
168 new GeoDistanceLinkWeight(deviceService) : null;
169 setDefaultLinkWeight(weight);
170 }
171 log.info(FORMAT, linkWeightFunction);
172 }
173
alshabib339a3d92014-09-26 17:54:32 -0700174 @Override
175 public Topology currentTopology() {
176 return current;
177 }
178
179 @Override
180 public boolean isLatest(Topology topology) {
181 // Topology is current only if it is the same as our current topology
182 return topology == current;
183 }
184
185 @Override
186 public TopologyGraph getGraph(Topology topology) {
187 return defaultTopology(topology).getGraph();
188 }
189
190 @Override
191 public Set<TopologyCluster> getClusters(Topology topology) {
192 return defaultTopology(topology).getClusters();
193 }
194
195 @Override
196 public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
197 return defaultTopology(topology).getCluster(clusterId);
198 }
199
200 @Override
201 public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
202 return defaultTopology(topology).getClusterDevices(cluster);
203 }
204
205 @Override
206 public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
207 return defaultTopology(topology).getClusterLinks(cluster);
208 }
209
210 @Override
211 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
212 return defaultTopology(topology).getPaths(src, dst);
213 }
214
215 @Override
216 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700217 LinkWeight weight) {
alshabib339a3d92014-09-26 17:54:32 -0700218 return defaultTopology(topology).getPaths(src, dst, weight);
219 }
220
221 @Override
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700222 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
223 return defaultTopology(topology).getDisjointPaths(src, dst);
224 }
225
226 @Override
227 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
228 LinkWeight weight) {
229 return defaultTopology(topology).getDisjointPaths(src, dst, weight);
230 }
231
232 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700233 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
234 Map<Link, Object> riskProfile) {
235 return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700236 }
237
238 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700239 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
240 LinkWeight weight, Map<Link, Object> riskProfile) {
241 return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700242 }
243
244 @Override
alshabib339a3d92014-09-26 17:54:32 -0700245 public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
246 return defaultTopology(topology).isInfrastructure(connectPoint);
247 }
248
249 @Override
250 public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
251 return defaultTopology(topology).isBroadcastPoint(connectPoint);
252 }
253
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700254 private boolean isBroadcastPoint(ConnectPoint connectPoint) {
255 // Any non-infrastructure, i.e. edge points are assumed to be OK.
256 if (!current.isInfrastructure(connectPoint)) {
257 return true;
258 }
259
260 // Find the cluster to which the device belongs.
261 TopologyCluster cluster = current.getCluster(connectPoint.deviceId());
262 checkArgument(cluster != null, "No cluster found for device %s", connectPoint.deviceId());
263
264 // If the broadcast set is null or empty, or if the point explicitly
265 // belongs to it, return true;
266 Set<ConnectPoint> points = broadcastPoints.get(cluster.root().deviceId());
267 return isNullOrEmpty(points) || points.contains(connectPoint);
268 }
269
alshabib339a3d92014-09-26 17:54:32 -0700270 @Override
271 public TopologyEvent updateTopology(ProviderId providerId,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700272 GraphDescription graphDescription,
273 List<Event> reasons) {
alshabib339a3d92014-09-26 17:54:32 -0700274 // First off, make sure that what we're given is indeed newer than
275 // what we already have.
276 if (current != null && graphDescription.timestamp() < current.time()) {
277 return null;
278 }
279
280 // Have the default topology construct self from the description data.
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700281 DefaultTopology newTopology =
282 new DefaultTopology(providerId, graphDescription, this::isBroadcastPoint);
283 updateBroadcastPoints(newTopology);
alshabib339a3d92014-09-26 17:54:32 -0700284
285 // Promote the new topology to current and return a ready-to-send event.
286 synchronized (this) {
287 current = newTopology;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700288 return new TopologyEvent(TOPOLOGY_CHANGED, current, reasons);
alshabib339a3d92014-09-26 17:54:32 -0700289 }
290 }
291
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700292 private void updateBroadcastPoints(DefaultTopology topology) {
293 // Remove any broadcast trees rooted by devices for which we are master.
294 Set<DeviceId> toRemove = broadcastPoints.keySet().stream()
295 .filter(mastershipService::isLocalMaster)
296 .collect(Collectors.toSet());
297
298 // Update the broadcast trees rooted by devices for which we are master.
299 topology.getClusters().forEach(c -> {
300 toRemove.remove(c.root().deviceId());
301 if (mastershipService.isLocalMaster(c.root().deviceId())) {
302 broadcastPoints.put(c.root().deviceId(),
303 topology.broadcastPoints(c.id()));
304 }
305 });
306
307 toRemove.forEach(broadcastPoints::remove);
308 }
309
alshabib339a3d92014-09-26 17:54:32 -0700310 // Validates the specified topology and returns it as a default
311 private DefaultTopology defaultTopology(Topology topology) {
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700312 checkArgument(topology instanceof DefaultTopology,
313 "Topology class %s not supported", topology.getClass());
314 return (DefaultTopology) topology;
alshabib339a3d92014-09-26 17:54:32 -0700315 }
316
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800317 @Override
318 public void setDefaultLinkWeight(LinkWeight linkWeight) {
319 DefaultTopology.setDefaultLinkWeight(linkWeight);
320 }
321
322 @Override
323 public void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
324 DefaultTopology.setDefaultGraphPathSearch(graphPathSearch);
325 }
326
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700327 private class InternalBroadcastPointListener
328 implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
329 @Override
330 public void event(EventuallyConsistentMapEvent<DeviceId, Set<ConnectPoint>> event) {
331 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
332 if (!event.value().isEmpty()) {
333 log.info("Cluster rooted at {} has {} broadcast-points; #{}",
334 event.key(), event.value().size(), event.value().hashCode());
335 }
336 }
337 }
338 }
alshabib339a3d92014-09-26 17:54:32 -0700339}