blob: 9f15b6c4c3b761ced112b72cba38c287c3c58270 [file] [log] [blame]
Andrea Campanella545edb42018-03-20 16:37:29 -07001/*
2 * Copyright 2015-present Open Networking Foundation
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 */
16package org.onosproject.mcast.impl;
17
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -070018import com.google.common.base.Objects;
Andrea Campanella545edb42018-03-20 16:37:29 -070019import com.google.common.collect.ImmutableSet;
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -070020import com.google.common.collect.Sets;
Andrea Campanella545edb42018-03-20 16:37:29 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.IpAddress;
28import org.onosproject.event.AbstractListenerManager;
29import org.onosproject.mcast.api.McastEvent;
30import org.onosproject.mcast.api.McastListener;
31import org.onosproject.mcast.api.McastRoute;
32import org.onosproject.mcast.api.McastRouteData;
33import org.onosproject.mcast.api.McastStore;
34import org.onosproject.mcast.api.McastStoreDelegate;
35import org.onosproject.mcast.api.MulticastRouteService;
36import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.Host;
38import org.onosproject.net.HostId;
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -070039import org.onosproject.net.HostLocation;
Andrea Campanella545edb42018-03-20 16:37:29 -070040import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostListener;
42import org.onosproject.net.host.HostService;
43import org.slf4j.Logger;
44
45import java.util.HashSet;
46import java.util.Optional;
47import java.util.Set;
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -070048import java.util.stream.Collectors;
Andrea Campanella545edb42018-03-20 16:37:29 -070049
50import static com.google.common.base.Preconditions.checkNotNull;
51import static org.slf4j.LoggerFactory.getLogger;
52
53/**
54 * An implementation of a multicast route table.
55 */
56@Component(immediate = true)
57@Service
58public class MulticastRouteManager
59 extends AbstractListenerManager<McastEvent, McastListener>
60 implements MulticastRouteService {
61 //TODO: add MulticastRouteAdminService
62
63 private Logger log = getLogger(getClass());
64
65 private final McastStoreDelegate delegate = new InternalMcastStoreDelegate();
66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected McastStore store;
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected HostService hostService;
72
73 private HostListener hostListener = new InternalHostListener();
74
75 @Activate
76 public void activate() {
77 hostService.addListener(hostListener);
78 eventDispatcher.addSink(McastEvent.class, listenerRegistry);
79 store.setDelegate(delegate);
80 log.info("Started");
81 }
82
83 @Deactivate
84 public void deactivate() {
85 hostService.removeListener(hostListener);
86 store.unsetDelegate(delegate);
87 eventDispatcher.removeSink(McastEvent.class);
88 log.info("Stopped");
89 }
90
91 @Override
92 public void add(McastRoute route) {
93 checkNotNull(route, "Route cannot be null");
94 store.storeRoute(route);
95 }
96
97 @Override
98 public void remove(McastRoute route) {
99 checkNotNull(route, "Route cannot be null");
100 if (checkRoute(route)) {
101 store.removeRoute(route);
102 }
103 }
104
105 @Override
106 public Set<McastRoute> getRoutes() {
107 return store.getRoutes();
108 }
109
110 @Override
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700111 public Set<McastRoute> getRoute(IpAddress groupIp, IpAddress sourceIp) {
112 // Let's transform it into an optional
113 final Optional<IpAddress> source = Optional.ofNullable(sourceIp);
114 return store.getRoutes().stream()
115 .filter(route -> route.group().equals(groupIp) &&
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200116 Objects.equal(route.source(), source))
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700117 .collect(Collectors.toSet());
Andrea Campanella545edb42018-03-20 16:37:29 -0700118 }
119
120 @Override
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200121 public void addSource(McastRoute route, HostId source) {
Andrea Campanella545edb42018-03-20 16:37:29 -0700122 checkNotNull(route, "Route cannot be null");
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200123 checkNotNull(source, "Source cannot be null");
Andrea Campanella545edb42018-03-20 16:37:29 -0700124 if (checkRoute(route)) {
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200125 Set<ConnectPoint> sources = new HashSet<>();
126 Host host = hostService.getHost(source);
127 if (host != null) {
128 sources.addAll(host.locations());
129 }
130 store.storeSource(route, source, sources);
131 }
132 }
133
134 @Override
135 public void addSources(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints) {
136 checkNotNull(route, "Route cannot be null");
137 checkNotNull(hostId, "HostId cannot be null");
138 checkNotNull(connectPoints, "Sources cannot be null");
139 if (checkRoute(route)) {
140 store.storeSource(route, hostId, connectPoints);
141 }
142 }
143
144 @Override
145 public void addSources(McastRoute route, Set<ConnectPoint> sources) {
146 checkNotNull(route, "Route cannot be null");
147 checkNotNull(sources, "sources cannot be null");
148 if (checkRoute(route)) {
149 store.storeSources(route, sources);
Andrea Campanella545edb42018-03-20 16:37:29 -0700150 }
151 }
152
153 @Override
154 public void removeSources(McastRoute route) {
155 checkNotNull(route, "Route cannot be null");
156 if (checkRoute(route)) {
157 store.removeSources(route);
158 }
159 }
160
161 @Override
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200162 public void removeSource(McastRoute route, HostId source) {
Andrea Campanella545edb42018-03-20 16:37:29 -0700163 checkNotNull(route, "Route cannot be null");
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200164 checkNotNull(source, "Source cannot be null");
Andrea Campanella545edb42018-03-20 16:37:29 -0700165 if (checkRoute(route)) {
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200166 store.removeSource(route, source);
Andrea Campanella545edb42018-03-20 16:37:29 -0700167 }
168 }
169
170 @Override
171 public void addSink(McastRoute route, HostId hostId) {
172 if (checkRoute(route)) {
173 Set<ConnectPoint> sinks = new HashSet<>();
174 Host host = hostService.getHost(hostId);
175 if (host != null) {
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200176 sinks.addAll(host.locations());
Andrea Campanella545edb42018-03-20 16:37:29 -0700177 }
178 store.addSink(route, hostId, sinks);
179 }
180
181 }
182
183 @Override
Andrea Campanella644a8a62018-03-21 19:08:21 -0700184 public void addSinks(McastRoute route, HostId hostId, Set<ConnectPoint> sinks) {
185 if (checkRoute(route)) {
186 store.addSink(route, hostId, sinks);
187 }
188
189 }
190
191 @Override
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200192 public void addSinks(McastRoute route, Set<ConnectPoint> sinks) {
Andrea Campanella545edb42018-03-20 16:37:29 -0700193 checkNotNull(route, "Route cannot be null");
194 checkNotNull(sinks, "Sinks cannot be null");
195 if (checkRoute(route)) {
196 store.addSinks(route, sinks);
197 }
198 }
199
200 @Override
201 public void removeSinks(McastRoute route) {
202 checkNotNull(route, "Route cannot be null");
203 if (checkRoute(route)) {
204 store.removeSinks(route);
205 }
206 }
207
208 @Override
209 public void removeSink(McastRoute route, HostId hostId) {
210 checkNotNull(route, "Route cannot be null");
211 checkNotNull(hostId, "Host cannot be null");
212 if (checkRoute(route)) {
213 store.removeSink(route, hostId);
214 }
215 }
216
217 @Override
Andrea Campanella545edb42018-03-20 16:37:29 -0700218 public void removeSinks(McastRoute route, Set<ConnectPoint> connectPoints) {
219 checkNotNull(route, "Route cannot be null");
220 if (checkRoute(route)) {
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700221 store.removeSinks(route, connectPoints);
Andrea Campanella545edb42018-03-20 16:37:29 -0700222 }
223 }
224
225 @Override
226 public McastRouteData routeData(McastRoute route) {
227 checkNotNull(route, "Route cannot be null");
228 return checkRoute(route) ? store.getRouteData(route) : null;
229 }
230
231 @Override
232 public Set<ConnectPoint> sources(McastRoute route) {
233 checkNotNull(route, "Route cannot be null");
234 return checkRoute(route) ? store.sourcesFor(route) : ImmutableSet.of();
235 }
236
237 @Override
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200238 public Set<ConnectPoint> sources(McastRoute route, HostId hostId) {
239 checkNotNull(route, "Route cannot be null");
240 return checkRoute(route) ? store.sourcesFor(route, hostId) : ImmutableSet.of();
241 }
242
243 @Override
Andrea Campanella545edb42018-03-20 16:37:29 -0700244 public Set<ConnectPoint> sinks(McastRoute route) {
245 checkNotNull(route, "Route cannot be null");
246 return checkRoute(route) ? store.sinksFor(route) : ImmutableSet.of();
247 }
248
249 @Override
250 public Set<ConnectPoint> sinks(McastRoute route, HostId hostId) {
251 checkNotNull(route, "Route cannot be null");
252 return checkRoute(route) ? store.sinksFor(route, hostId) : ImmutableSet.of();
253 }
254
255 @Override
256 public Set<ConnectPoint> nonHostSinks(McastRoute route) {
257 checkNotNull(route, "Route cannot be null");
258 return checkRoute(route) ? store.sinksFor(route, HostId.NONE) : ImmutableSet.of();
259 }
260
261 private class InternalMcastStoreDelegate implements McastStoreDelegate {
262 @Override
263 public void notify(McastEvent event) {
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700264 log.debug("Notify event: {}", event);
Andrea Campanella545edb42018-03-20 16:37:29 -0700265 post(event);
266 }
267 }
268
269 private boolean checkRoute(McastRoute route) {
270 if (store.getRoutes().contains(route)) {
271 return true;
272 } else {
273 log.warn("Route {} is not present in the store, please add it", route);
274 }
275 return false;
276 }
277
278 private class InternalHostListener implements HostListener {
279
280 @Override
281 public void event(HostEvent event) {
282 HostId hostId = event.subject().id();
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700283 log.debug("Host event: {}", event);
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200284 Set<McastRoute> routesForSource = routesForSource(hostId);
285 Set<McastRoute> routesForSink = routesForSink(hostId);
Andrea Campanella545edb42018-03-20 16:37:29 -0700286 switch (event.type()) {
287 case HOST_ADDED:
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700288 //the host is added, if it already comes with some locations let's use them
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200289 if (!routesForSource.isEmpty()) {
290 eventAddSources(hostId, event.subject().locations(), routesForSource);
291 }
292 if (!routesForSink.isEmpty()) {
293 eventAddSinks(hostId, event.subject().locations(), routesForSink);
294 }
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700295 break;
Andrea Campanella545edb42018-03-20 16:37:29 -0700296 case HOST_MOVED:
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700297 //both subjects must be null or the system is in an incoherent state
298 if ((event.prevSubject() != null && event.subject() != null)) {
299 //we compute the difference between old locations and new ones and remove the previous
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200300 Set<HostLocation> removedConnectPoint = Sets.difference(event.prevSubject().locations(),
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700301 event.subject().locations()).immutableCopy();
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200302 if (!removedConnectPoint.isEmpty()) {
303 if (!routesForSource.isEmpty()) {
304 eventRemoveSources(hostId, removedConnectPoint, routesForSource);
305 }
306 if (!routesForSink.isEmpty()) {
307 eventRemoveSinks(hostId, removedConnectPoint, routesForSink);
308 }
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700309 }
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200310 Set<HostLocation> addedConnectPoints = Sets.difference(event.subject().locations(),
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700311 event.prevSubject().locations()).immutableCopy();
312 //if the host now has some new locations we add them to the sinks set
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200313 if (!addedConnectPoints.isEmpty()) {
314 if (!routesForSource.isEmpty()) {
315 eventAddSources(hostId, addedConnectPoints, routesForSource);
316 }
317 if (!routesForSink.isEmpty()) {
318 eventAddSinks(hostId, addedConnectPoints, routesForSink);
319 }
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700320 }
Andrea Campanella545edb42018-03-20 16:37:29 -0700321 }
322 break;
323 case HOST_REMOVED:
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200324 // Removing all the connect points for that specific host
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700325 // even if the locations are 0 we keep
326 // the host information in the route in case it shows up again
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200327 if (!routesForSource.isEmpty()) {
328 eventRemoveSources(hostId, event.subject().locations(), routesForSource);
329 }
330 if (!routesForSink.isEmpty()) {
331 eventRemoveSinks(hostId, event.subject().locations(), routesForSink);
332 }
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700333 break;
334 case HOST_UPDATED:
Andrea Campanella545edb42018-03-20 16:37:29 -0700335 default:
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700336 log.debug("Host event {} not handled", event.type());
Andrea Campanella545edb42018-03-20 16:37:29 -0700337 }
338 }
339 }
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700340
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200341 //Finds the route for which a host is source
342 private Set<McastRoute> routesForSource(HostId hostId) {
343 // Filter by host id
344 return store.getRoutes().stream().filter(mcastRoute -> store.getRouteData(mcastRoute)
345 .sources().containsKey(hostId)).collect(Collectors.toSet());
346 }
347
348 //Finds the route for which a host is sink
349 private Set<McastRoute> routesForSink(HostId hostId) {
350 return store.getRoutes().stream().filter(mcastRoute -> store.getRouteData(mcastRoute)
351 .sinks().containsKey(hostId)).collect(Collectors.toSet());
352 }
353
354 //Removes sources for a given host event
355 private void eventRemoveSources(HostId hostId, Set<HostLocation> removedSources, Set<McastRoute> routesForSource) {
356 Set<ConnectPoint> sources = new HashSet<>();
357 // Build sink using host location
358 sources.addAll(removedSources);
359 // Remove from each route the provided sinks
360 routesForSource.forEach(route -> store.removeSources(route, hostId, sources));
361 }
362
363 //Adds the sources for a given host event
364 private void eventAddSources(HostId hostId, Set<HostLocation> addedSources, Set<McastRoute> routesForSource) {
365 Set<ConnectPoint> sources = new HashSet<>();
366 // Build source using host location
367 sources.addAll(addedSources);
368 // Add to each route the provided sources
369 routesForSource.forEach(route -> store.storeSource(route, hostId, sources));
370 }
371
372 //Remove sinks for a given host event
373 private void eventRemoveSinks(HostId hostId, Set<HostLocation> removedSinks, Set<McastRoute> routesForSinks) {
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700374 Set<ConnectPoint> sinks = new HashSet<>();
375 // Build sink using host location
376 sinks.addAll(removedSinks);
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200377 // Remove from each route the provided sinks
378 routesForSinks.forEach(route -> store.removeSinks(route, hostId, sinks));
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700379 }
380
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200381 //Adds the sinks for a given host event
382 private void eventAddSinks(HostId hostId, Set<HostLocation> addedSinks, Set<McastRoute> routesForSinks) {
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700383 Set<ConnectPoint> sinks = new HashSet<>();
384 // Build sink using host location
385 sinks.addAll(addedSinks);
Andrea Campanella0ddf9b82018-04-27 15:54:42 +0200386 // Add to each route the provided sinks
387 routesForSinks.forEach(route -> store.addSink(route, hostId, sinks));
Andrea Campanella7c8bcdf2018-03-26 23:29:11 -0700388 }
Andrea Campanella545edb42018-03-20 16:37:29 -0700389}