blob: b52618ca3a2356e3857d6caee27c553f84907b6f [file] [log] [blame]
Jonathan Hart96c146b2017-02-24 16:32:00 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Jonathan Hart96c146b2017-02-24 16:32:00 -08003 *
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
Ray Milkey69ec8712017-08-08 13:00:43 -070017package org.onosproject.routeservice.store;
Jonathan Hart96c146b2017-02-24 16:32:00 -080018
Jordan Halterman3b137372018-04-30 14:42:41 -070019import java.util.Collection;
20import java.util.Collections;
21import java.util.Map;
22import java.util.concurrent.ExecutorService;
23import java.util.function.Consumer;
24import java.util.stream.Collectors;
25
Daniel Ginsburg83b76452018-06-09 01:43:59 +030026import com.google.common.collect.Sets;
Jonathan Hart96c146b2017-02-24 16:32:00 -080027import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070029import org.onosproject.cluster.NodeId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080030import org.onlab.util.KryoNamespace;
Ray Milkey69ec8712017-08-08 13:00:43 -070031import org.onosproject.routeservice.InternalRouteEvent;
32import org.onosproject.routeservice.Route;
33import org.onosproject.routeservice.RouteSet;
34import org.onosproject.routeservice.RouteStoreDelegate;
35import org.onosproject.routeservice.RouteTableId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080036import org.onosproject.store.serializers.KryoNamespaces;
Jordan Halterman3b137372018-04-30 14:42:41 -070037import org.onosproject.store.service.ConsistentMultimap;
Jonathan Hart1f67d282017-05-25 14:23:01 -070038import org.onosproject.store.service.DistributedPrimitive;
Jordan Halterman3b137372018-04-30 14:42:41 -070039import org.onosproject.store.service.MultimapEvent;
40import org.onosproject.store.service.MultimapEventListener;
Jonathan Hart96c146b2017-02-24 16:32:00 -080041import org.onosproject.store.service.Serializer;
42import org.onosproject.store.service.StorageService;
43import org.onosproject.store.service.Versioned;
44
Jonathan Hart96c146b2017-02-24 16:32:00 -080045import static com.google.common.base.Preconditions.checkNotNull;
46
47/**
48 * Default implementation of a route table based on a consistent map.
49 */
50public class DefaultRouteTable implements RouteTable {
51
52 private final RouteTableId id;
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070053
54 // The route map stores RawRoute instead of Route to translate the polymorphic IpPrefix and IpAddress types
55 // into monomorphic types (specifically String). Using strings in the stored RawRoute is necessary to ensure
56 // the serialized bytes are consistent whether e.g. IpAddress or Ip4Address is used when storing a route.
57 private final ConsistentMultimap<String, RawRoute> routes;
58
Jonathan Hart96c146b2017-02-24 16:32:00 -080059 private final RouteStoreDelegate delegate;
Jonathan Hart1f67d282017-05-25 14:23:01 -070060 private final ExecutorService executor;
Jonathan Hart96c146b2017-02-24 16:32:00 -080061 private final RouteTableListener listener = new RouteTableListener();
62
Jonathan Hart1f67d282017-05-25 14:23:01 -070063 private final Consumer<DistributedPrimitive.Status> statusChangeListener;
64
Jonathan Hart96c146b2017-02-24 16:32:00 -080065 /**
66 * Creates a new route table.
67 *
68 * @param id route table ID
69 * @param delegate route store delegate to notify of events
70 * @param storageService storage service
Jonathan Hart1f67d282017-05-25 14:23:01 -070071 * @param executor executor service
Jonathan Hart96c146b2017-02-24 16:32:00 -080072 */
73 public DefaultRouteTable(RouteTableId id, RouteStoreDelegate delegate,
Jonathan Hart1f67d282017-05-25 14:23:01 -070074 StorageService storageService, ExecutorService executor) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080075 this.delegate = checkNotNull(delegate);
76 this.id = checkNotNull(id);
77 this.routes = buildRouteMap(checkNotNull(storageService));
Jonathan Hart1f67d282017-05-25 14:23:01 -070078 this.executor = checkNotNull(executor);
Jonathan Hart96c146b2017-02-24 16:32:00 -080079
Jonathan Hart1f67d282017-05-25 14:23:01 -070080 statusChangeListener = status -> {
81 if (status.equals(DistributedPrimitive.Status.ACTIVE)) {
82 executor.execute(this::notifyExistingRoutes);
83 }
84 };
85 routes.addStatusChangeListener(statusChangeListener);
86
87 notifyExistingRoutes();
88
Jordan Halterman3b137372018-04-30 14:42:41 -070089 routes.addListener(listener, executor);
Jonathan Hart1f67d282017-05-25 14:23:01 -070090 }
91
92 private void notifyExistingRoutes() {
Jordan Halterman3b137372018-04-30 14:42:41 -070093 getRoutes().forEach(routeSet -> delegate.notify(
94 new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, routeSet)));
Jonathan Hart96c146b2017-02-24 16:32:00 -080095 }
96
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070097 private ConsistentMultimap<String, RawRoute> buildRouteMap(StorageService storageService) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080098 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
99 .register(KryoNamespaces.API)
100 .register(Route.class)
101 .register(Route.Source.class)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700102 .register(RawRoute.class)
Jonathan Hart96c146b2017-02-24 16:32:00 -0800103 .build();
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700104 return storageService.<String, RawRoute>consistentMultimapBuilder()
Jonathan Hart96c146b2017-02-24 16:32:00 -0800105 .withName("onos-routes-" + id.name())
106 .withRelaxedReadConsistency()
107 .withSerializer(Serializer.using(routeTableSerializer))
108 .build();
109 }
110
111 @Override
112 public RouteTableId id() {
113 return id;
114 }
115
116 @Override
117 public void shutdown() {
Jonathan Hart1f67d282017-05-25 14:23:01 -0700118 routes.removeStatusChangeListener(statusChangeListener);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800119 routes.removeListener(listener);
120 }
121
122 @Override
123 public void destroy() {
124 shutdown();
125 routes.destroy();
126 }
127
128 @Override
129 public void update(Route route) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700130 routes.put(route.prefix().toString(), new RawRoute(route));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800131 }
132
133 @Override
134 public void remove(Route route) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700135 routes.remove(route.prefix().toString(), new RawRoute(route));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800136 }
137
138 @Override
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300139 public void replace(Route route) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700140 routes.replaceValues(route.prefix().toString(), Sets.newHashSet(new RawRoute(route)));
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300141 }
142
143 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -0800144 public Collection<RouteSet> getRoutes() {
Jordan Halterman3b137372018-04-30 14:42:41 -0700145 return routes.stream()
146 .map(Map.Entry::getValue)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700147 .collect(Collectors.groupingBy(RawRoute::prefix))
Jordan Halterman3b137372018-04-30 14:42:41 -0700148 .entrySet()
149 .stream()
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700150 .map(entry -> new RouteSet(id,
151 IpPrefix.valueOf(entry.getKey()),
152 entry.getValue().stream().map(RawRoute::route).collect(Collectors.toSet())))
Jordan Halterman3b137372018-04-30 14:42:41 -0700153 .collect(Collectors.toList());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800154 }
155
156 @Override
157 public RouteSet getRoutes(IpPrefix prefix) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700158 Versioned<Collection<? extends RawRoute>> routeSet = routes.get(prefix.toString());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800159 if (routeSet != null) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700160 return new RouteSet(id, prefix, routeSet.value().stream().map(RawRoute::route).collect(Collectors.toSet()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800161 }
162 return null;
163 }
164
165 @Override
166 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
Jordan Halterman3b137372018-04-30 14:42:41 -0700167 return routes.stream()
168 .map(Map.Entry::getValue)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700169 .filter(r -> IpAddress.valueOf(r.nextHop()).equals(nextHop))
170 .map(RawRoute::route)
Jordan Halterman3b137372018-04-30 14:42:41 -0700171 .collect(Collectors.toSet());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800172 }
173
174 private class RouteTableListener
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700175 implements MultimapEventListener<String, RawRoute> {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800176
177 private InternalRouteEvent createRouteEvent(
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700178 InternalRouteEvent.Type type, MultimapEvent<String, RawRoute> event) {
179 Collection<? extends RawRoute> currentRoutes = Versioned.valueOrNull(routes.get(event.key()));
Jordan Halterman3b137372018-04-30 14:42:41 -0700180 return new InternalRouteEvent(type, new RouteSet(
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700181 id, IpPrefix.valueOf(event.key()), currentRoutes != null ?
182 currentRoutes.stream().map(RawRoute::route).collect(Collectors.toSet())
183 : Collections.emptySet()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800184 }
185
186 @Override
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700187 public void event(MultimapEvent<String, RawRoute> event) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800188 InternalRouteEvent ire = null;
189 switch (event.type()) {
190 case INSERT:
191 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
192 break;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800193 case REMOVE:
194 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
195 break;
196 default:
197 break;
198 }
Jordan Halterman3b137372018-04-30 14:42:41 -0700199 delegate.notify(ire);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800200 }
201 }
Jonathan Hart1f67d282017-05-25 14:23:01 -0700202
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700203 /**
204 * Represents a route object stored in the underlying ConsistentMultimap.
205 */
206 private static class RawRoute {
207 private Route.Source source;
208 private String prefix;
209 private String nextHop;
210 private NodeId sourceNode;
211
212 RawRoute(Route route) {
213 this.source = route.source();
214 this.prefix = route.prefix().toString();
215 this.nextHop = route.nextHop().toString();
216 this.sourceNode = route.sourceNode();
217 }
218
219 String prefix() {
220 return prefix;
221 }
222
223 String nextHop() {
224 return nextHop;
225 }
226
227 Route route() {
228 return new Route(source, IpPrefix.valueOf(prefix), IpAddress.valueOf(nextHop), sourceNode);
229 }
230 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800231}