blob: f052f04d4926fd139f05d32597424b32705816ff [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;
pier594843a2018-12-21 22:02:25 +010022import java.util.Objects;
Jordan Halterman3b137372018-04-30 14:42:41 -070023import java.util.concurrent.ExecutorService;
24import java.util.function.Consumer;
25import java.util.stream.Collectors;
26
Daniel Ginsburg83b76452018-06-09 01:43:59 +030027import com.google.common.collect.Sets;
Jonathan Hart96c146b2017-02-24 16:32:00 -080028import org.onlab.packet.IpAddress;
29import org.onlab.packet.IpPrefix;
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070030import org.onosproject.cluster.NodeId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080031import org.onlab.util.KryoNamespace;
Ray Milkey69ec8712017-08-08 13:00:43 -070032import org.onosproject.routeservice.InternalRouteEvent;
33import org.onosproject.routeservice.Route;
34import org.onosproject.routeservice.RouteSet;
35import org.onosproject.routeservice.RouteStoreDelegate;
36import org.onosproject.routeservice.RouteTableId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080037import org.onosproject.store.serializers.KryoNamespaces;
Jordan Halterman3b137372018-04-30 14:42:41 -070038import org.onosproject.store.service.ConsistentMultimap;
Jonathan Hart1f67d282017-05-25 14:23:01 -070039import org.onosproject.store.service.DistributedPrimitive;
Jordan Halterman3b137372018-04-30 14:42:41 -070040import org.onosproject.store.service.MultimapEvent;
41import org.onosproject.store.service.MultimapEventListener;
Jonathan Hart96c146b2017-02-24 16:32:00 -080042import org.onosproject.store.service.Serializer;
43import org.onosproject.store.service.StorageService;
44import org.onosproject.store.service.Versioned;
45
pier594843a2018-12-21 22:02:25 +010046import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart96c146b2017-02-24 16:32:00 -080047import static com.google.common.base.Preconditions.checkNotNull;
48
49/**
50 * Default implementation of a route table based on a consistent map.
51 */
52public class DefaultRouteTable implements RouteTable {
53
54 private final RouteTableId id;
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070055
56 // The route map stores RawRoute instead of Route to translate the polymorphic IpPrefix and IpAddress types
57 // into monomorphic types (specifically String). Using strings in the stored RawRoute is necessary to ensure
58 // the serialized bytes are consistent whether e.g. IpAddress or Ip4Address is used when storing a route.
59 private final ConsistentMultimap<String, RawRoute> routes;
60
Jonathan Hart96c146b2017-02-24 16:32:00 -080061 private final RouteStoreDelegate delegate;
Jonathan Hart1f67d282017-05-25 14:23:01 -070062 private final ExecutorService executor;
Jonathan Hart96c146b2017-02-24 16:32:00 -080063 private final RouteTableListener listener = new RouteTableListener();
64
Jonathan Hart1f67d282017-05-25 14:23:01 -070065 private final Consumer<DistributedPrimitive.Status> statusChangeListener;
66
Jonathan Hart96c146b2017-02-24 16:32:00 -080067 /**
68 * Creates a new route table.
69 *
70 * @param id route table ID
71 * @param delegate route store delegate to notify of events
72 * @param storageService storage service
Jonathan Hart1f67d282017-05-25 14:23:01 -070073 * @param executor executor service
Jonathan Hart96c146b2017-02-24 16:32:00 -080074 */
75 public DefaultRouteTable(RouteTableId id, RouteStoreDelegate delegate,
Jonathan Hart1f67d282017-05-25 14:23:01 -070076 StorageService storageService, ExecutorService executor) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080077 this.delegate = checkNotNull(delegate);
78 this.id = checkNotNull(id);
79 this.routes = buildRouteMap(checkNotNull(storageService));
Jonathan Hart1f67d282017-05-25 14:23:01 -070080 this.executor = checkNotNull(executor);
Jonathan Hart96c146b2017-02-24 16:32:00 -080081
Jonathan Hart1f67d282017-05-25 14:23:01 -070082 statusChangeListener = status -> {
83 if (status.equals(DistributedPrimitive.Status.ACTIVE)) {
84 executor.execute(this::notifyExistingRoutes);
85 }
86 };
87 routes.addStatusChangeListener(statusChangeListener);
88
89 notifyExistingRoutes();
90
Jordan Halterman3b137372018-04-30 14:42:41 -070091 routes.addListener(listener, executor);
Jonathan Hart1f67d282017-05-25 14:23:01 -070092 }
93
94 private void notifyExistingRoutes() {
Jordan Halterman3b137372018-04-30 14:42:41 -070095 getRoutes().forEach(routeSet -> delegate.notify(
96 new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, routeSet)));
Jonathan Hart96c146b2017-02-24 16:32:00 -080097 }
98
Jordan Halterman5f5ceb62018-10-01 23:17:57 -070099 private ConsistentMultimap<String, RawRoute> buildRouteMap(StorageService storageService) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800100 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
101 .register(KryoNamespaces.API)
102 .register(Route.class)
103 .register(Route.Source.class)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700104 .register(RawRoute.class)
Jonathan Hart96c146b2017-02-24 16:32:00 -0800105 .build();
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700106 return storageService.<String, RawRoute>consistentMultimapBuilder()
Jonathan Hart96c146b2017-02-24 16:32:00 -0800107 .withName("onos-routes-" + id.name())
108 .withRelaxedReadConsistency()
109 .withSerializer(Serializer.using(routeTableSerializer))
110 .build();
111 }
112
113 @Override
114 public RouteTableId id() {
115 return id;
116 }
117
118 @Override
119 public void shutdown() {
Jonathan Hart1f67d282017-05-25 14:23:01 -0700120 routes.removeStatusChangeListener(statusChangeListener);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800121 routes.removeListener(listener);
122 }
123
124 @Override
125 public void destroy() {
126 shutdown();
127 routes.destroy();
128 }
129
130 @Override
131 public void update(Route route) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700132 routes.put(route.prefix().toString(), new RawRoute(route));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800133 }
134
135 @Override
136 public void remove(Route route) {
Jordan Haltermanfd822362018-12-22 17:01:18 -0800137 getRoutes(route.prefix())
138 .routes()
139 .stream()
140 .filter(r -> r.equals(route))
141 .findAny()
142 .ifPresent(matchRoute -> {
143 routes.remove(matchRoute.prefix().toString(), new RawRoute(matchRoute));
144 });
Jonathan Hart96c146b2017-02-24 16:32:00 -0800145 }
146
147 @Override
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300148 public void replace(Route route) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700149 routes.replaceValues(route.prefix().toString(), Sets.newHashSet(new RawRoute(route)));
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300150 }
151
152 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -0800153 public Collection<RouteSet> getRoutes() {
Jordan Halterman3b137372018-04-30 14:42:41 -0700154 return routes.stream()
155 .map(Map.Entry::getValue)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700156 .collect(Collectors.groupingBy(RawRoute::prefix))
Jordan Halterman3b137372018-04-30 14:42:41 -0700157 .entrySet()
158 .stream()
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700159 .map(entry -> new RouteSet(id,
160 IpPrefix.valueOf(entry.getKey()),
161 entry.getValue().stream().map(RawRoute::route).collect(Collectors.toSet())))
Jordan Halterman3b137372018-04-30 14:42:41 -0700162 .collect(Collectors.toList());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800163 }
164
165 @Override
166 public RouteSet getRoutes(IpPrefix prefix) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700167 Versioned<Collection<? extends RawRoute>> routeSet = routes.get(prefix.toString());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800168 if (routeSet != null) {
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700169 return new RouteSet(id, prefix, routeSet.value().stream().map(RawRoute::route).collect(Collectors.toSet()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800170 }
171 return null;
172 }
173
174 @Override
175 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
Jordan Halterman3b137372018-04-30 14:42:41 -0700176 return routes.stream()
177 .map(Map.Entry::getValue)
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700178 .filter(r -> IpAddress.valueOf(r.nextHop()).equals(nextHop))
179 .map(RawRoute::route)
Jordan Halterman3b137372018-04-30 14:42:41 -0700180 .collect(Collectors.toSet());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800181 }
182
183 private class RouteTableListener
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700184 implements MultimapEventListener<String, RawRoute> {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800185
186 private InternalRouteEvent createRouteEvent(
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700187 InternalRouteEvent.Type type, MultimapEvent<String, RawRoute> event) {
188 Collection<? extends RawRoute> currentRoutes = Versioned.valueOrNull(routes.get(event.key()));
Jordan Halterman3b137372018-04-30 14:42:41 -0700189 return new InternalRouteEvent(type, new RouteSet(
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700190 id, IpPrefix.valueOf(event.key()), currentRoutes != null ?
191 currentRoutes.stream().map(RawRoute::route).collect(Collectors.toSet())
192 : Collections.emptySet()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800193 }
194
195 @Override
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700196 public void event(MultimapEvent<String, RawRoute> event) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800197 InternalRouteEvent ire = null;
198 switch (event.type()) {
199 case INSERT:
200 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
201 break;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800202 case REMOVE:
203 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
204 break;
205 default:
206 break;
207 }
Jordan Halterman3b137372018-04-30 14:42:41 -0700208 delegate.notify(ire);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800209 }
210 }
Jonathan Hart1f67d282017-05-25 14:23:01 -0700211
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700212 /**
213 * Represents a route object stored in the underlying ConsistentMultimap.
214 */
215 private static class RawRoute {
216 private Route.Source source;
217 private String prefix;
218 private String nextHop;
219 private NodeId sourceNode;
220
221 RawRoute(Route route) {
222 this.source = route.source();
223 this.prefix = route.prefix().toString();
224 this.nextHop = route.nextHop().toString();
225 this.sourceNode = route.sourceNode();
226 }
227
228 String prefix() {
229 return prefix;
230 }
231
232 String nextHop() {
233 return nextHop;
234 }
235
236 Route route() {
237 return new Route(source, IpPrefix.valueOf(prefix), IpAddress.valueOf(nextHop), sourceNode);
238 }
pier594843a2018-12-21 22:02:25 +0100239
240 public int hashCode() {
241 return Objects.hash(prefix, nextHop);
242 }
243
244 @Override
245 public boolean equals(Object other) {
246 if (this == other) {
247 return true;
248 }
249
250 if (!(other instanceof RawRoute)) {
251 return false;
252 }
253
254 RawRoute that = (RawRoute) other;
255
256 return Objects.equals(this.prefix, that.prefix) &&
257 Objects.equals(this.nextHop, that.nextHop);
258 }
259
260 @Override
261 public String toString() {
262 return toStringHelper(this)
263 .add("prefix", prefix)
264 .add("nextHop", nextHop)
265 .toString();
266 }
267
Jordan Halterman5f5ceb62018-10-01 23:17:57 -0700268 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800269}