blob: 368d65eefed774b6b646c33b42282a806c6f53a8 [file] [log] [blame]
Jonathan Hartbfc5c482016-04-05 18:57:00 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Jonathan Hartbfc5c482016-04-05 18:57:00 -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 */
16
17package org.onosproject.incubator.net.routing.impl;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.IpAddress;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070026import org.onosproject.event.ListenerService;
Jonathan Hartfd176612016-04-11 10:42:10 -070027import org.onosproject.incubator.net.routing.NextHop;
Jonathan Hart79a0abd2017-02-23 19:13:27 -080028import org.onosproject.incubator.net.routing.NextHopData;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070029import org.onosproject.incubator.net.routing.ResolvedRoute;
30import org.onosproject.incubator.net.routing.Route;
31import org.onosproject.incubator.net.routing.RouteAdminService;
32import org.onosproject.incubator.net.routing.RouteEvent;
33import org.onosproject.incubator.net.routing.RouteListener;
34import org.onosproject.incubator.net.routing.RouteService;
35import org.onosproject.incubator.net.routing.RouteStore;
36import org.onosproject.incubator.net.routing.RouteStoreDelegate;
37import org.onosproject.incubator.net.routing.RouteTableId;
38import org.onosproject.net.Host;
39import org.onosproject.net.host.HostEvent;
40import org.onosproject.net.host.HostListener;
41import org.onosproject.net.host.HostService;
42import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
45import javax.annotation.concurrent.GuardedBy;
46import java.util.Collection;
47import java.util.Collections;
48import java.util.HashMap;
49import java.util.Map;
50import java.util.Optional;
51import java.util.Set;
52import java.util.concurrent.BlockingQueue;
53import java.util.concurrent.ExecutorService;
54import java.util.concurrent.LinkedBlockingQueue;
55import java.util.concurrent.ThreadFactory;
56import java.util.function.Function;
57import java.util.stream.Collectors;
58
59import static java.util.concurrent.Executors.newSingleThreadExecutor;
60import static org.onlab.util.Tools.groupedThreads;
61
62/**
63 * Implementation of the unicast route service.
64 */
65@Service
66@Component
67public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
68 RouteService, RouteAdminService {
69
70 private final Logger log = LoggerFactory.getLogger(getClass());
71
72 private RouteStoreDelegate delegate = new InternalRouteStoreDelegate();
73 private InternalHostListener hostListener = new InternalHostListener();
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected RouteStore routeStore;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected HostService hostService;
80
81 @GuardedBy(value = "this")
82 private Map<RouteListener, ListenerQueue> listeners = new HashMap<>();
83
84 private ThreadFactory threadFactory;
85
86 @Activate
87 protected void activate() {
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070088 threadFactory = groupedThreads("onos/route", "listener-%d", log);
Jonathan Hartbfc5c482016-04-05 18:57:00 -070089
90 routeStore.setDelegate(delegate);
91 hostService.addListener(hostListener);
Jonathan Hartbfc5c482016-04-05 18:57:00 -070092 }
93
94 @Deactivate
95 protected void deactivate() {
96 listeners.values().forEach(l -> l.stop());
97
98 routeStore.unsetDelegate(delegate);
99 hostService.removeListener(hostListener);
100 }
101
102 /**
103 * {@inheritDoc}
104 *
105 * In a departure from other services in ONOS, calling addListener will
106 * cause all current routes to be pushed to the listener before any new
107 * events are sent. This allows a listener to easily get the exact set of
108 * routes without worrying about missing any.
109 *
110 * @param listener listener to be added
111 */
112 @Override
113 public void addListener(RouteListener listener) {
114 synchronized (this) {
115 log.debug("Synchronizing current routes to new listener");
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700116 ListenerQueue l = createListenerQueue(listener);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700117 routeStore.getRouteTables().forEach(table -> {
118 Collection<Route> routes = routeStore.getRoutes(table);
119 if (routes != null) {
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700120 routes.forEach(route -> {
121 NextHopData nextHopData = routeStore.getNextHop(route.nextHop());
Pier Luigia44d18a2017-01-13 14:00:23 -0800122 if (nextHopData != null) {
Charles Chane4d13102016-11-08 15:38:44 -0800123 l.post(new RouteEvent(RouteEvent.Type.ROUTE_ADDED,
Pier Luigia44d18a2017-01-13 14:00:23 -0800124 new ResolvedRoute(route, nextHopData.mac(),
125 nextHopData.location())));
126 }
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700127 });
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700128 }
129 });
130
131 listeners.put(listener, l);
132
133 l.start();
134 log.debug("Route synchronization complete");
135 }
136 }
137
138 @Override
139 public void removeListener(RouteListener listener) {
140 synchronized (this) {
141 ListenerQueue l = listeners.remove(listener);
142 if (l != null) {
143 l.stop();
144 }
145 }
146 }
147
148 /**
149 * Posts an event to all listeners.
150 *
151 * @param event event
152 */
153 private void post(RouteEvent event) {
Jonathan Hartfd176612016-04-11 10:42:10 -0700154 log.debug("Sending event {}", event);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700155 synchronized (this) {
156 listeners.values().forEach(l -> l.post(event));
157 }
158 }
159
160 @Override
161 public Map<RouteTableId, Collection<Route>> getAllRoutes() {
162 return routeStore.getRouteTables().stream()
163 .collect(Collectors.toMap(Function.identity(),
164 table -> (table == null) ?
165 Collections.emptySet() : routeStore.getRoutes(table)));
166 }
167
168 @Override
169 public Route longestPrefixMatch(IpAddress ip) {
170 return routeStore.longestPrefixMatch(ip);
171 }
172
173 @Override
Jonathan Hartfd176612016-04-11 10:42:10 -0700174 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
175 return routeStore.getRoutesForNextHop(nextHop);
176 }
177
178 @Override
179 public Set<NextHop> getNextHops() {
180 return routeStore.getNextHops().entrySet().stream()
Charles Chanc78a0982016-11-09 16:52:11 -0800181 .map(entry -> new NextHop(entry.getKey(), entry.getValue()))
Jonathan Hartfd176612016-04-11 10:42:10 -0700182 .collect(Collectors.toSet());
183 }
184
185 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700186 public void update(Collection<Route> routes) {
187 synchronized (this) {
188 routes.forEach(route -> {
Jonathan Hartfd176612016-04-11 10:42:10 -0700189 log.debug("Received update {}", route);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700190 routeStore.updateRoute(route);
191 resolve(route);
192 });
193 }
194 }
195
196 @Override
197 public void withdraw(Collection<Route> routes) {
198 synchronized (this) {
Jonathan Hartfd176612016-04-11 10:42:10 -0700199 routes.forEach(route -> {
Charles Chanb21d69a2016-11-11 17:46:14 -0800200 log.debug("Received withdraw {}", route);
Jonathan Hartfd176612016-04-11 10:42:10 -0700201 routeStore.removeRoute(route);
202 });
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700203 }
204 }
205
206 private void resolve(Route route) {
207 // Monitor the IP address for updates of the MAC address
208 hostService.startMonitoringIp(route.nextHop());
209
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700210 NextHopData nextHopData = routeStore.getNextHop(route.nextHop());
211 if (nextHopData == null) {
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700212 Set<Host> hosts = hostService.getHostsByIp(route.nextHop());
213 Optional<Host> host = hosts.stream().findFirst();
214 if (host.isPresent()) {
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700215 nextHopData = NextHopData.fromHost(host.get());
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700216 }
217 }
218
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700219 if (nextHopData != null) {
220 routeStore.updateNextHop(route.nextHop(), nextHopData);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700221 }
222 }
223
224 private void hostUpdated(Host host) {
225 synchronized (this) {
226 for (IpAddress ip : host.ipAddresses()) {
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700227 routeStore.updateNextHop(ip, NextHopData.fromHost(host));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700228 }
229 }
230 }
231
232 private void hostRemoved(Host host) {
233 synchronized (this) {
234 for (IpAddress ip : host.ipAddresses()) {
Charles Chan8fe9f4c2016-10-24 16:46:25 -0700235 routeStore.removeNextHop(ip, NextHopData.fromHost(host));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700236 }
237 }
238 }
239
240 /**
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700241 * Creates a new listener queue.
242 *
243 * @param listener route listener
244 * @return listener queue
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700245 */
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700246 ListenerQueue createListenerQueue(RouteListener listener) {
247 return new DefaultListenerQueue(listener);
248 }
249
250 /**
251 * Default route listener queue.
252 */
253 private class DefaultListenerQueue implements ListenerQueue {
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700254
255 private final ExecutorService executorService;
256 private final BlockingQueue<RouteEvent> queue;
257 private final RouteListener listener;
258
259 /**
260 * Creates a new listener queue.
261 *
262 * @param listener route listener to queue updates for
263 */
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700264 public DefaultListenerQueue(RouteListener listener) {
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700265 this.listener = listener;
266 queue = new LinkedBlockingQueue<>();
267 executorService = newSingleThreadExecutor(threadFactory);
268 }
269
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700270 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700271 public void post(RouteEvent event) {
272 queue.add(event);
273 }
274
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700275 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700276 public void start() {
277 executorService.execute(this::poll);
278 }
279
Jonathan Hart6c2e7962016-04-11 13:54:09 -0700280 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700281 public void stop() {
282 executorService.shutdown();
283 }
284
285 private void poll() {
Jonathan Hartf79ab482016-08-19 14:20:50 -0700286 while (true) {
287 try {
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700288 listener.event(queue.take());
Jonathan Hartf79ab482016-08-19 14:20:50 -0700289 } catch (InterruptedException e) {
290 log.info("Route listener event thread shutting down: {}", e.getMessage());
291 break;
292 } catch (Exception e) {
293 log.warn("Exception during route event handler", e);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700294 }
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700295 }
296 }
297
298 }
299
300 /**
301 * Delegate to receive events from the route store.
302 */
303 private class InternalRouteStoreDelegate implements RouteStoreDelegate {
304 @Override
305 public void notify(RouteEvent event) {
306 post(event);
307 }
308 }
309
310 /**
311 * Internal listener for host events.
312 */
313 private class InternalHostListener implements HostListener {
314 @Override
315 public void event(HostEvent event) {
316 switch (event.type()) {
317 case HOST_ADDED:
318 case HOST_UPDATED:
319 hostUpdated(event.subject());
320 break;
321 case HOST_REMOVED:
322 hostRemoved(event.subject());
323 break;
324 case HOST_MOVED:
325 break;
326 default:
327 break;
328 }
329 }
330 }
331
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700332}