blob: 476f3c0dd60e653c348952b7f1a9d7f85f594927 [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 Halterman5c7913d42018-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
26import com.google.common.collect.ImmutableSet;
Daniel Ginsburgc1d6aaf2018-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;
30import 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 Halterman5c7913d42018-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 Halterman5c7913d42018-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 Halterman5c7913d42018-04-30 14:42:41 -070053 private final ConsistentMultimap<IpPrefix, Route> routes;
Jonathan Hart96c146b2017-02-24 16:32:00 -080054 private final RouteStoreDelegate delegate;
Jonathan Hart1f67d282017-05-25 14:23:01 -070055 private final ExecutorService executor;
Jonathan Hart96c146b2017-02-24 16:32:00 -080056 private final RouteTableListener listener = new RouteTableListener();
57
Jonathan Hart1f67d282017-05-25 14:23:01 -070058 private final Consumer<DistributedPrimitive.Status> statusChangeListener;
59
Jonathan Hart96c146b2017-02-24 16:32:00 -080060 /**
61 * Creates a new route table.
62 *
63 * @param id route table ID
64 * @param delegate route store delegate to notify of events
65 * @param storageService storage service
Jonathan Hart1f67d282017-05-25 14:23:01 -070066 * @param executor executor service
Jonathan Hart96c146b2017-02-24 16:32:00 -080067 */
68 public DefaultRouteTable(RouteTableId id, RouteStoreDelegate delegate,
Jonathan Hart1f67d282017-05-25 14:23:01 -070069 StorageService storageService, ExecutorService executor) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080070 this.delegate = checkNotNull(delegate);
71 this.id = checkNotNull(id);
72 this.routes = buildRouteMap(checkNotNull(storageService));
Jonathan Hart1f67d282017-05-25 14:23:01 -070073 this.executor = checkNotNull(executor);
Jonathan Hart96c146b2017-02-24 16:32:00 -080074
Jonathan Hart1f67d282017-05-25 14:23:01 -070075 statusChangeListener = status -> {
76 if (status.equals(DistributedPrimitive.Status.ACTIVE)) {
77 executor.execute(this::notifyExistingRoutes);
78 }
79 };
80 routes.addStatusChangeListener(statusChangeListener);
81
82 notifyExistingRoutes();
83
Jordan Halterman5c7913d42018-04-30 14:42:41 -070084 routes.addListener(listener, executor);
Jonathan Hart1f67d282017-05-25 14:23:01 -070085 }
86
87 private void notifyExistingRoutes() {
Jordan Halterman5c7913d42018-04-30 14:42:41 -070088 getRoutes().forEach(routeSet -> delegate.notify(
89 new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, routeSet)));
Jonathan Hart96c146b2017-02-24 16:32:00 -080090 }
91
Jordan Halterman5c7913d42018-04-30 14:42:41 -070092 private ConsistentMultimap<IpPrefix, Route> buildRouteMap(StorageService storageService) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080093 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
94 .register(KryoNamespaces.API)
95 .register(Route.class)
96 .register(Route.Source.class)
97 .build();
Jordan Halterman5c7913d42018-04-30 14:42:41 -070098 return storageService.<IpPrefix, Route>consistentMultimapBuilder()
Jonathan Hart96c146b2017-02-24 16:32:00 -080099 .withName("onos-routes-" + id.name())
100 .withRelaxedReadConsistency()
101 .withSerializer(Serializer.using(routeTableSerializer))
102 .build();
103 }
104
105 @Override
106 public RouteTableId id() {
107 return id;
108 }
109
110 @Override
111 public void shutdown() {
Jonathan Hart1f67d282017-05-25 14:23:01 -0700112 routes.removeStatusChangeListener(statusChangeListener);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800113 routes.removeListener(listener);
114 }
115
116 @Override
117 public void destroy() {
118 shutdown();
119 routes.destroy();
120 }
121
122 @Override
123 public void update(Route route) {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700124 routes.put(route.prefix(), route);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800125 }
126
127 @Override
128 public void remove(Route route) {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700129 routes.remove(route.prefix(), route);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800130 }
131
132 @Override
Daniel Ginsburgc1d6aaf2018-06-09 01:43:59 +0300133 public void replace(Route route) {
134 routes.replaceValues(route.prefix(), Sets.newHashSet(route));
135 }
136
137 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -0800138 public Collection<RouteSet> getRoutes() {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700139 return routes.stream()
140 .map(Map.Entry::getValue)
141 .collect(Collectors.groupingBy(Route::prefix))
142 .entrySet()
143 .stream()
144 .map(entry -> new RouteSet(id, entry.getKey(), ImmutableSet.copyOf(entry.getValue())))
145 .collect(Collectors.toList());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800146 }
147
148 @Override
149 public RouteSet getRoutes(IpPrefix prefix) {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700150 Versioned<Collection<? extends Route>> routeSet = routes.get(prefix);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800151
152 if (routeSet != null) {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700153 return new RouteSet(id, prefix, ImmutableSet.copyOf(routeSet.value()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800154 }
155 return null;
156 }
157
158 @Override
159 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700160 return routes.stream()
161 .map(Map.Entry::getValue)
162 .filter(r -> r.nextHop().equals(nextHop))
163 .collect(Collectors.toSet());
Jonathan Hart96c146b2017-02-24 16:32:00 -0800164 }
165
166 private class RouteTableListener
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700167 implements MultimapEventListener<IpPrefix, Route> {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800168
169 private InternalRouteEvent createRouteEvent(
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700170 InternalRouteEvent.Type type, MultimapEvent<IpPrefix, Route> event) {
171 Collection<? extends Route> currentRoutes = Versioned.valueOrNull(routes.get(event.key()));
172 return new InternalRouteEvent(type, new RouteSet(
173 id, event.key(), currentRoutes != null ? ImmutableSet.copyOf(currentRoutes) : Collections.emptySet()));
Jonathan Hart96c146b2017-02-24 16:32:00 -0800174 }
175
176 @Override
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700177 public void event(MultimapEvent<IpPrefix, Route> event) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800178 InternalRouteEvent ire = null;
179 switch (event.type()) {
180 case INSERT:
181 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
182 break;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800183 case REMOVE:
184 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
185 break;
186 default:
187 break;
188 }
Jordan Halterman5c7913d42018-04-30 14:42:41 -0700189 delegate.notify(ire);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800190 }
191 }
Jonathan Hart1f67d282017-05-25 14:23:01 -0700192
Jonathan Hart96c146b2017-02-24 16:32:00 -0800193}