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