blob: cbfacad74d7e6de4d4453f0c78950df59d20b3f1 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
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;
43import org.onosproject.net.topology.LinkWeight;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080044import org.onosproject.net.topology.MetricLinkWeight;
45import org.onosproject.net.topology.PathAdminService;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.topology.Topology;
47import org.onosproject.net.topology.TopologyCluster;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080048import org.onosproject.net.topology.TopologyEdge;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.topology.TopologyEvent;
50import org.onosproject.net.topology.TopologyGraph;
51import org.onosproject.net.topology.TopologyStore;
52import org.onosproject.net.topology.TopologyStoreDelegate;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080053import org.onosproject.net.topology.TopologyVertex;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.store.AbstractStore;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070055import org.onosproject.store.serializers.KryoNamespaces;
56import org.onosproject.store.service.EventuallyConsistentMap;
57import org.onosproject.store.service.EventuallyConsistentMapEvent;
58import org.onosproject.store.service.EventuallyConsistentMapListener;
59import org.onosproject.store.service.LogicalClockService;
60import org.onosproject.store.service.StorageService;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080061import org.osgi.service.component.ComponentContext;
alshabib339a3d92014-09-26 17:54:32 -070062import org.slf4j.Logger;
63
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080064import java.util.Collections;
65import java.util.Dictionary;
66import java.util.List;
67import java.util.Map;
68import java.util.Objects;
69import java.util.Set;
70import java.util.stream.Collectors;
71
72import static com.google.common.base.Preconditions.checkArgument;
73import static org.onlab.util.Tools.get;
74import static org.onlab.util.Tools.isNullOrEmpty;
75import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
76import static org.slf4j.LoggerFactory.getLogger;
77
alshabib339a3d92014-09-26 17:54:32 -070078/**
79 * Manages inventory of topology snapshots using trivial in-memory
80 * structures implementation.
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070081 * <p>
Brian O'Connor44008532014-12-04 16:41:36 -080082 * Note: This component is not distributed per-se. It runs on every
83 * instance and feeds off of other distributed stores.
alshabib339a3d92014-09-26 17:54:32 -070084 */
alshabib339a3d92014-09-26 17:54:32 -070085@Component(immediate = true)
86@Service
87public class DistributedTopologyStore
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070088 extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080089 implements TopologyStore, PathAdminService {
alshabib339a3d92014-09-26 17:54:32 -070090
91 private final Logger log = getLogger(getClass());
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080092
93 private static final String FORMAT = "Settings: linkWeightFunction={}";
94
Jonathan Hart29858af2014-10-29 15:33:18 -070095 private volatile DefaultTopology current =
96 new DefaultTopology(ProviderId.NONE,
Thomas Vachuska320c58f2015-08-05 10:42:32 -070097 new DefaultGraphDescription(0L, System.currentTimeMillis(),
Sho SHIMIZU21d00692016-08-15 11:15:28 -070098 Collections.emptyList(),
99 Collections.emptyList()));
alshabib339a3d92014-09-26 17:54:32 -0700100
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected StorageService storageService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected LogicalClockService clockService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected MastershipService mastershipService;
109
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected ComponentConfigService configService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected DeviceService deviceService;
115
116 private static final String HOP_COUNT = "hopCount";
117 private static final String LINK_METRIC = "linkMetric";
118 private static final String GEO_DISTANCE = "geoDistance";
119
120 private static final String DEFAULT_LINK_WEIGHT_FUNCTION = "hopCount";
121 @Property(name = "linkWeightFunction", value = DEFAULT_LINK_WEIGHT_FUNCTION,
122 label = "Default link-weight function: hopCount, linkMetric, geoDistance")
123 private String linkWeightFunction = DEFAULT_LINK_WEIGHT_FUNCTION;
124
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700125 // Cluster root to broadcast points bindings to allow convergence to
126 // a shared broadcast tree; node that is the master of the cluster root
127 // is the primary.
128 private EventuallyConsistentMap<DeviceId, Set<ConnectPoint>> broadcastPoints;
129
130 private EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> listener =
131 new InternalBroadcastPointListener();
132
alshabib339a3d92014-09-26 17:54:32 -0700133 @Activate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800134 protected void activate() {
135 configService.registerProperties(getClass());
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700136 KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
137 .register(KryoNamespaces.API);
138
139 broadcastPoints = storageService.<DeviceId, Set<ConnectPoint>>eventuallyConsistentMapBuilder()
140 .withName("onos-broadcast-trees")
141 .withSerializer(hostSerializer)
142 .withTimestampProvider((k, v) -> clockService.getTimestamp())
143 .build();
144 broadcastPoints.addListener(listener);
alshabib339a3d92014-09-26 17:54:32 -0700145 log.info("Started");
146 }
147
148 @Deactivate
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800149 protected void deactivate() {
150 configService.unregisterProperties(getClass(), false);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700151 broadcastPoints.removeListener(listener);
152 broadcastPoints.destroy();
alshabib339a3d92014-09-26 17:54:32 -0700153 log.info("Stopped");
154 }
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700155
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800156 @Modified
157 protected void modified(ComponentContext context) {
158 Dictionary<?, ?> properties = context.getProperties();
159
160 String newLinkWeightFunction = get(properties, "linkWeightFunction");
161 if (newLinkWeightFunction != null &&
162 !Objects.equals(newLinkWeightFunction, linkWeightFunction)) {
163 linkWeightFunction = newLinkWeightFunction;
164 LinkWeight weight = linkWeightFunction.equals(LINK_METRIC) ?
165 new MetricLinkWeight() :
166 linkWeightFunction.equals(GEO_DISTANCE) ?
167 new GeoDistanceLinkWeight(deviceService) : null;
168 setDefaultLinkWeight(weight);
169 }
170 log.info(FORMAT, linkWeightFunction);
171 }
172
alshabib339a3d92014-09-26 17:54:32 -0700173 @Override
174 public Topology currentTopology() {
175 return current;
176 }
177
178 @Override
179 public boolean isLatest(Topology topology) {
180 // Topology is current only if it is the same as our current topology
181 return topology == current;
182 }
183
184 @Override
185 public TopologyGraph getGraph(Topology topology) {
186 return defaultTopology(topology).getGraph();
187 }
188
189 @Override
190 public Set<TopologyCluster> getClusters(Topology topology) {
191 return defaultTopology(topology).getClusters();
192 }
193
194 @Override
195 public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
196 return defaultTopology(topology).getCluster(clusterId);
197 }
198
199 @Override
200 public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
201 return defaultTopology(topology).getClusterDevices(cluster);
202 }
203
204 @Override
205 public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
206 return defaultTopology(topology).getClusterLinks(cluster);
207 }
208
209 @Override
210 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
211 return defaultTopology(topology).getPaths(src, dst);
212 }
213
214 @Override
215 public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700216 LinkWeight weight) {
alshabib339a3d92014-09-26 17:54:32 -0700217 return defaultTopology(topology).getPaths(src, dst, weight);
218 }
219
220 @Override
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700221 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
222 return defaultTopology(topology).getDisjointPaths(src, dst);
223 }
224
225 @Override
226 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
227 LinkWeight weight) {
228 return defaultTopology(topology).getDisjointPaths(src, dst, weight);
229 }
230
231 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700232 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
233 Map<Link, Object> riskProfile) {
234 return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700235 }
236
237 @Override
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700238 public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
239 LinkWeight weight, Map<Link, Object> riskProfile) {
240 return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile);
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700241 }
242
243 @Override
alshabib339a3d92014-09-26 17:54:32 -0700244 public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
245 return defaultTopology(topology).isInfrastructure(connectPoint);
246 }
247
248 @Override
249 public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
250 return defaultTopology(topology).isBroadcastPoint(connectPoint);
251 }
252
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700253 private boolean isBroadcastPoint(ConnectPoint connectPoint) {
254 // Any non-infrastructure, i.e. edge points are assumed to be OK.
255 if (!current.isInfrastructure(connectPoint)) {
256 return true;
257 }
258
259 // Find the cluster to which the device belongs.
260 TopologyCluster cluster = current.getCluster(connectPoint.deviceId());
261 checkArgument(cluster != null, "No cluster found for device %s", connectPoint.deviceId());
262
263 // If the broadcast set is null or empty, or if the point explicitly
264 // belongs to it, return true;
265 Set<ConnectPoint> points = broadcastPoints.get(cluster.root().deviceId());
266 return isNullOrEmpty(points) || points.contains(connectPoint);
267 }
268
alshabib339a3d92014-09-26 17:54:32 -0700269 @Override
270 public TopologyEvent updateTopology(ProviderId providerId,
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700271 GraphDescription graphDescription,
272 List<Event> reasons) {
alshabib339a3d92014-09-26 17:54:32 -0700273 // First off, make sure that what we're given is indeed newer than
274 // what we already have.
275 if (current != null && graphDescription.timestamp() < current.time()) {
276 return null;
277 }
278
279 // Have the default topology construct self from the description data.
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700280 DefaultTopology newTopology =
281 new DefaultTopology(providerId, graphDescription, this::isBroadcastPoint);
282 updateBroadcastPoints(newTopology);
alshabib339a3d92014-09-26 17:54:32 -0700283
284 // Promote the new topology to current and return a ready-to-send event.
285 synchronized (this) {
286 current = newTopology;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700287 return new TopologyEvent(TOPOLOGY_CHANGED, current, reasons);
alshabib339a3d92014-09-26 17:54:32 -0700288 }
289 }
290
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700291 private void updateBroadcastPoints(DefaultTopology topology) {
292 // Remove any broadcast trees rooted by devices for which we are master.
293 Set<DeviceId> toRemove = broadcastPoints.keySet().stream()
294 .filter(mastershipService::isLocalMaster)
295 .collect(Collectors.toSet());
296
297 // Update the broadcast trees rooted by devices for which we are master.
298 topology.getClusters().forEach(c -> {
299 toRemove.remove(c.root().deviceId());
300 if (mastershipService.isLocalMaster(c.root().deviceId())) {
301 broadcastPoints.put(c.root().deviceId(),
302 topology.broadcastPoints(c.id()));
303 }
304 });
305
306 toRemove.forEach(broadcastPoints::remove);
307 }
308
alshabib339a3d92014-09-26 17:54:32 -0700309 // Validates the specified topology and returns it as a default
310 private DefaultTopology defaultTopology(Topology topology) {
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700311 checkArgument(topology instanceof DefaultTopology,
312 "Topology class %s not supported", topology.getClass());
313 return (DefaultTopology) topology;
alshabib339a3d92014-09-26 17:54:32 -0700314 }
315
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800316 @Override
317 public void setDefaultLinkWeight(LinkWeight linkWeight) {
318 DefaultTopology.setDefaultLinkWeight(linkWeight);
319 }
320
321 @Override
322 public void setDefaultGraphPathSearch(GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
323 DefaultTopology.setDefaultGraphPathSearch(graphPathSearch);
324 }
325
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700326 private class InternalBroadcastPointListener
327 implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
328 @Override
329 public void event(EventuallyConsistentMapEvent<DeviceId, Set<ConnectPoint>> event) {
330 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
331 if (!event.value().isEmpty()) {
Madan Jampanib6e884b2016-06-23 01:34:15 -0700332 log.debug("Cluster rooted at {} has {} broadcast-points; #{}",
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700333 event.key(), event.value().size(), event.value().hashCode());
334 }
335 }
336 }
337 }
alshabib339a3d92014-09-26 17:54:32 -0700338}