blob: 1231abec07b1812db23f14a6efd90f2765ff82ac [file] [log] [blame]
Jonathan Hart96c146b2017-02-24 16:32:00 -08001/*
2 * Copyright 2017-present Open Networking Laboratory
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 */
16
17package org.onosproject.incubator.store.routing.impl;
18
19import org.onlab.packet.IpAddress;
20import org.onlab.packet.IpPrefix;
21import org.onlab.util.KryoNamespace;
22import org.onosproject.incubator.net.routing.InternalRouteEvent;
23import org.onosproject.incubator.net.routing.Route;
24import org.onosproject.incubator.net.routing.RouteSet;
25import org.onosproject.incubator.net.routing.RouteStoreDelegate;
26import org.onosproject.incubator.net.routing.RouteTableId;
27import org.onosproject.store.serializers.KryoNamespaces;
28import org.onosproject.store.service.ConsistentMap;
Jonathan Hart1f67d282017-05-25 14:23:01 -070029import org.onosproject.store.service.DistributedPrimitive;
Jonathan Hart96c146b2017-02-24 16:32:00 -080030import org.onosproject.store.service.MapEvent;
31import org.onosproject.store.service.MapEventListener;
32import org.onosproject.store.service.Serializer;
33import org.onosproject.store.service.StorageService;
34import org.onosproject.store.service.Versioned;
35
36import java.util.Collection;
37import java.util.Collections;
38import java.util.HashSet;
39import java.util.Set;
Jonathan Hart1f67d282017-05-25 14:23:01 -070040import java.util.concurrent.ExecutorService;
41import java.util.function.Consumer;
Jonathan Hart96c146b2017-02-24 16:32:00 -080042import java.util.stream.Collectors;
43
44import static com.google.common.base.Preconditions.checkNotNull;
45
46/**
47 * Default implementation of a route table based on a consistent map.
48 */
49public class DefaultRouteTable implements RouteTable {
50
51 private final RouteTableId id;
52 private final ConsistentMap<IpPrefix, Set<Route>> routes;
53 private final RouteStoreDelegate delegate;
Jonathan Hart1f67d282017-05-25 14:23:01 -070054 private final ExecutorService executor;
Jonathan Hart96c146b2017-02-24 16:32:00 -080055 private final RouteTableListener listener = new RouteTableListener();
56
Jonathan Hart1f67d282017-05-25 14:23:01 -070057 private final Consumer<DistributedPrimitive.Status> statusChangeListener;
58
Jonathan Hart96c146b2017-02-24 16:32:00 -080059 /**
60 * Creates a new route table.
61 *
62 * @param id route table ID
63 * @param delegate route store delegate to notify of events
64 * @param storageService storage service
Jonathan Hart1f67d282017-05-25 14:23:01 -070065 * @param executor executor service
Jonathan Hart96c146b2017-02-24 16:32:00 -080066 */
67 public DefaultRouteTable(RouteTableId id, RouteStoreDelegate delegate,
Jonathan Hart1f67d282017-05-25 14:23:01 -070068 StorageService storageService, ExecutorService executor) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080069 this.delegate = checkNotNull(delegate);
70 this.id = checkNotNull(id);
71 this.routes = buildRouteMap(checkNotNull(storageService));
Jonathan Hart1f67d282017-05-25 14:23:01 -070072 this.executor = checkNotNull(executor);
Jonathan Hart96c146b2017-02-24 16:32:00 -080073
Jonathan Hart1f67d282017-05-25 14:23:01 -070074 statusChangeListener = status -> {
75 if (status.equals(DistributedPrimitive.Status.ACTIVE)) {
76 executor.execute(this::notifyExistingRoutes);
77 }
78 };
79 routes.addStatusChangeListener(statusChangeListener);
80
81 notifyExistingRoutes();
82
83 routes.addListener(listener);
84 }
85
86 private void notifyExistingRoutes() {
Jonathan Hart96c146b2017-02-24 16:32:00 -080087 routes.entrySet().stream()
88 .map(e -> new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED,
89 new RouteSet(id, e.getKey(), e.getValue().value())))
90 .forEach(delegate::notify);
Jonathan Hart96c146b2017-02-24 16:32:00 -080091 }
92
93 private ConsistentMap<IpPrefix, Set<Route>> buildRouteMap(StorageService storageService) {
94 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
95 .register(KryoNamespaces.API)
96 .register(Route.class)
97 .register(Route.Source.class)
98 .build();
99 return storageService.<IpPrefix, Set<Route>>consistentMapBuilder()
100 .withName("onos-routes-" + id.name())
101 .withRelaxedReadConsistency()
102 .withSerializer(Serializer.using(routeTableSerializer))
103 .build();
104 }
105
106 @Override
107 public RouteTableId id() {
108 return id;
109 }
110
111 @Override
112 public void shutdown() {
Jonathan Hart1f67d282017-05-25 14:23:01 -0700113 routes.removeStatusChangeListener(statusChangeListener);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800114 routes.removeListener(listener);
115 }
116
117 @Override
118 public void destroy() {
119 shutdown();
120 routes.destroy();
121 }
122
123 @Override
124 public void update(Route route) {
125 routes.compute(route.prefix(), (prefix, set) -> {
126 if (set == null) {
127 set = new HashSet<>();
128 }
129 set.add(route);
130 return set;
131 });
132 }
133
134 @Override
135 public void remove(Route route) {
136 routes.compute(route.prefix(), (prefix, set) -> {
137 if (set != null) {
138 set.remove(route);
139 if (set.isEmpty()) {
140 return null;
141 }
142 return set;
143 }
144 return null;
145 });
146 }
147
148 @Override
149 public Collection<RouteSet> getRoutes() {
150 return routes.entrySet().stream()
151 .map(e -> new RouteSet(id, e.getKey(), e.getValue().value()))
152 .collect(Collectors.toSet());
153 }
154
155 @Override
156 public RouteSet getRoutes(IpPrefix prefix) {
157 Versioned<Set<Route>> routeSet = routes.get(prefix);
158
159 if (routeSet != null) {
160 return new RouteSet(id, prefix, routeSet.value());
161 }
162 return null;
163 }
164
165 @Override
166 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
167 // TODO index
168 return routes.values().stream()
169 .flatMap(v -> v.value().stream())
170 .filter(r -> r.nextHop().equals(nextHop))
171 .collect(Collectors.toSet());
172 }
173
174 private class RouteTableListener
175 implements MapEventListener<IpPrefix, Set<Route>> {
176
177 private InternalRouteEvent createRouteEvent(
178 InternalRouteEvent.Type type, MapEvent<IpPrefix, Set<Route>> event) {
179 Set<Route> currentRoutes =
180 (event.newValue() == null) ? Collections.emptySet() : event.newValue().value();
181 return new InternalRouteEvent(type, new RouteSet(id, event.key(), currentRoutes));
182 }
183
184 @Override
185 public void event(MapEvent<IpPrefix, Set<Route>> event) {
186 InternalRouteEvent ire = null;
187 switch (event.type()) {
188 case INSERT:
189 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
190 break;
191 case UPDATE:
192 if (event.newValue().value().size() > event.oldValue().value().size()) {
193 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
194 } else {
195 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
196 }
197 break;
198 case REMOVE:
199 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
200 break;
201 default:
202 break;
203 }
204 if (ire != null) {
205 delegate.notify(ire);
206 }
207 }
208 }
Jonathan Hart1f67d282017-05-25 14:23:01 -0700209
Jonathan Hart96c146b2017-02-24 16:32:00 -0800210}