blob: 33c8f969b313169e55d4147bd492615ebad8a9e5 [file] [log] [blame]
Mohammad Shahidaa7c1232017-08-09 11:13:15 +05301/*
2 * Copyright 2017-present Open Networking Foundation
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.packet.MacAddress;
22import org.onlab.util.KryoNamespace;
23import org.onosproject.incubator.net.routing.EvpnInternalRouteEvent;
24import org.onosproject.incubator.net.routing.EvpnPrefix;
25import org.onosproject.incubator.net.routing.EvpnRoute;
26import org.onosproject.incubator.net.routing.EvpnRouteSet;
27import org.onosproject.incubator.net.routing.EvpnRouteStoreDelegate;
Jonathan Harte9c0c6e2017-08-09 16:44:13 -070028import org.onosproject.incubator.net.routing.EvpnRouteTableId;
Mohammad Shahidaa7c1232017-08-09 11:13:15 +053029import org.onosproject.incubator.net.routing.EvpnTable;
30import org.onosproject.incubator.net.routing.Label;
Mohammad Shahidaa7c1232017-08-09 11:13:15 +053031import org.onosproject.incubator.net.routing.RouteDistinguisher;
Mohammad Shahidaa7c1232017-08-09 11:13:15 +053032import org.onosproject.incubator.net.routing.VpnRouteTarget;
33import org.onosproject.store.serializers.KryoNamespaces;
34import org.onosproject.store.service.ConsistentMap;
35import org.onosproject.store.service.DistributedPrimitive;
36import org.onosproject.store.service.MapEvent;
37import org.onosproject.store.service.MapEventListener;
38import org.onosproject.store.service.Serializer;
39import org.onosproject.store.service.StorageService;
40import org.onosproject.store.service.Versioned;
41
42import java.util.Collection;
43import java.util.Collections;
44import java.util.HashSet;
45import java.util.Set;
46import java.util.concurrent.ExecutorService;
47import java.util.function.Consumer;
48import java.util.stream.Collectors;
49
50import static com.google.common.base.Preconditions.checkNotNull;
51
52/**
53 * Default implementation of a route table based on a consistent map.
54 */
55public class EvpnRouteTable implements EvpnTable {
56
Jonathan Harte9c0c6e2017-08-09 16:44:13 -070057 private final EvpnRouteTableId id;
Mohammad Shahidaa7c1232017-08-09 11:13:15 +053058 private final ConsistentMap<EvpnPrefix, Set<EvpnRoute>> routes;
59 private final EvpnRouteStoreDelegate delegate;
60 private final ExecutorService executor;
61 private final RouteTableListener listener = new RouteTableListener();
62
63 private final Consumer<DistributedPrimitive.Status> statusChangeListener;
64
65 /**
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
71 * @param executor executor service
72 */
Jonathan Harte9c0c6e2017-08-09 16:44:13 -070073 public EvpnRouteTable(EvpnRouteTableId id, EvpnRouteStoreDelegate delegate,
Mohammad Shahidaa7c1232017-08-09 11:13:15 +053074 StorageService storageService, ExecutorService executor) {
75 this.delegate = checkNotNull(delegate);
76 this.id = checkNotNull(id);
77 this.routes = buildRouteMap(checkNotNull(storageService));
78 this.executor = checkNotNull(executor);
79
80 statusChangeListener = status -> {
81 if (status.equals(DistributedPrimitive.Status.ACTIVE)) {
82 executor.execute(this::notifyExistingRoutes);
83 }
84 };
85 routes.addStatusChangeListener(statusChangeListener);
86
87 notifyExistingRoutes();
88
89 routes.addListener(listener);
90 }
91
92 private void notifyExistingRoutes() {
93 routes.entrySet().stream()
94 .map(e -> new EvpnInternalRouteEvent(
95 EvpnInternalRouteEvent.Type.ROUTE_ADDED,
96 new EvpnRouteSet(id, e.getKey(), e.getValue().value())))
97 .forEach(delegate::notify);
98 }
99
100 private ConsistentMap<EvpnPrefix, Set<EvpnRoute>> buildRouteMap(StorageService
101 storageService) {
102 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
103 .register(KryoNamespaces.API)
104 .register(KryoNamespaces.MISC)
Mohammad Shahidaa7c1232017-08-09 11:13:15 +0530105 .register(EvpnRoute.class)
106 .register(EvpnPrefix.class)
107 .register(RouteDistinguisher.class)
108 .register(MacAddress.class)
109 .register(IpPrefix.class)
110 .register(EvpnRoute.Source.class)
111 .register(IpAddress.class)
112 .register(VpnRouteTarget.class)
113 .register(Label.class)
Jonathan Harte9c0c6e2017-08-09 16:44:13 -0700114 .register(EvpnRouteTableId.class)
Mohammad Shahidaa7c1232017-08-09 11:13:15 +0530115 .build();
116 return storageService.<EvpnPrefix, Set<EvpnRoute>>consistentMapBuilder()
117 .withName("onos-evpn-routes-" + id.name())
118 .withRelaxedReadConsistency()
119 .withSerializer(Serializer.using(routeTableSerializer))
120 .build();
121 }
122
123 @Override
Jonathan Harte9c0c6e2017-08-09 16:44:13 -0700124 public EvpnRouteTableId id() {
Mohammad Shahidaa7c1232017-08-09 11:13:15 +0530125 return id;
126 }
127
128 @Override
129 public void shutdown() {
130 routes.removeStatusChangeListener(statusChangeListener);
131 routes.removeListener(listener);
132 }
133
134 @Override
135 public void destroy() {
136 shutdown();
137 routes.destroy();
138 }
139
140 @Override
141 public void update(EvpnRoute route) {
142 routes.compute(route.evpnPrefix(), (prefix, set) -> {
143 if (set == null) {
144 set = new HashSet<>();
145 }
146 set.add(route);
147 return set;
148 });
149 }
150
151 @Override
152 public void remove(EvpnRoute route) {
153 routes.compute(route.evpnPrefix(), (prefix, set) -> {
154 if (set != null) {
155 set.remove(route);
156 if (set.isEmpty()) {
157 return null;
158 }
159 return set;
160 }
161 return null;
162 });
163 }
164
165 @Override
166 public Collection<EvpnRouteSet> getRoutes() {
167 return routes.entrySet().stream()
168 .map(e -> new EvpnRouteSet(id, e.getKey(), e.getValue().value()))
169 .collect(Collectors.toSet());
170 }
171
172 @Override
173 public EvpnRouteSet getRoutes(EvpnPrefix prefix) {
174 Versioned<Set<EvpnRoute>> routeSet = routes.get(prefix);
175
176 if (routeSet != null) {
177 return new EvpnRouteSet(id, prefix, routeSet.value());
178 }
179 return null;
180 }
181
182 @Override
183 public Collection<EvpnRoute> getRoutesForNextHop(IpAddress nextHop) {
184 // TODO index
185 return routes.values().stream()
186 .flatMap(v -> v.value().stream())
187 .filter(r -> r.nextHop().equals(nextHop))
188 .collect(Collectors.toSet());
189 }
190
191 private class RouteTableListener
192 implements MapEventListener<EvpnPrefix, Set<EvpnRoute>> {
193
194 private EvpnInternalRouteEvent createRouteEvent(
195 EvpnInternalRouteEvent.Type type, MapEvent<EvpnPrefix, Set<EvpnRoute>>
196 event) {
197 Set<EvpnRoute> currentRoutes =
198 (event.newValue() == null) ? Collections.emptySet() : event.newValue().value();
199 return new EvpnInternalRouteEvent(type, new EvpnRouteSet(id, event
200 .key(), currentRoutes));
201 }
202
203 @Override
204 public void event(MapEvent<EvpnPrefix, Set<EvpnRoute>> event) {
205 EvpnInternalRouteEvent ire = null;
206 switch (event.type()) {
207 case INSERT:
208 ire = createRouteEvent(EvpnInternalRouteEvent.Type.ROUTE_ADDED, event);
209 break;
210 case UPDATE:
211 if (event.newValue().value().size() > event.oldValue().value().size()) {
212 ire = createRouteEvent(EvpnInternalRouteEvent.Type.ROUTE_ADDED, event);
213 } else {
214 ire = createRouteEvent(EvpnInternalRouteEvent.Type.ROUTE_REMOVED, event);
215 }
216 break;
217 case REMOVE:
218 ire = createRouteEvent(EvpnInternalRouteEvent.Type.ROUTE_REMOVED, event);
219 break;
220 default:
221 break;
222 }
223 if (ire != null) {
224 delegate.notify(ire);
225 }
226 }
227 }
228
229}
230