blob: e9537d992f250ec6fbd2f6bda609716c267c3a57 [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;
29import org.onosproject.store.service.MapEvent;
30import org.onosproject.store.service.MapEventListener;
31import org.onosproject.store.service.Serializer;
32import org.onosproject.store.service.StorageService;
33import org.onosproject.store.service.Versioned;
34
35import java.util.Collection;
36import java.util.Collections;
37import java.util.HashSet;
38import java.util.Set;
39import java.util.stream.Collectors;
40
41import static com.google.common.base.Preconditions.checkNotNull;
42
43/**
44 * Default implementation of a route table based on a consistent map.
45 */
46public class DefaultRouteTable implements RouteTable {
47
48 private final RouteTableId id;
49 private final ConsistentMap<IpPrefix, Set<Route>> routes;
50 private final RouteStoreDelegate delegate;
51 private final RouteTableListener listener = new RouteTableListener();
52
53 /**
54 * Creates a new route table.
55 *
56 * @param id route table ID
57 * @param delegate route store delegate to notify of events
58 * @param storageService storage service
59 */
60 public DefaultRouteTable(RouteTableId id, RouteStoreDelegate delegate,
61 StorageService storageService) {
62 this.delegate = checkNotNull(delegate);
63 this.id = checkNotNull(id);
64 this.routes = buildRouteMap(checkNotNull(storageService));
65
66 routes.entrySet().stream()
67 .map(e -> new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED,
68 new RouteSet(id, e.getKey(), e.getValue().value())))
69 .forEach(delegate::notify);
70
71 routes.addListener(listener);
72 }
73
74 private ConsistentMap<IpPrefix, Set<Route>> buildRouteMap(StorageService storageService) {
75 KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
76 .register(KryoNamespaces.API)
77 .register(Route.class)
78 .register(Route.Source.class)
79 .build();
80 return storageService.<IpPrefix, Set<Route>>consistentMapBuilder()
81 .withName("onos-routes-" + id.name())
82 .withRelaxedReadConsistency()
83 .withSerializer(Serializer.using(routeTableSerializer))
84 .build();
85 }
86
87 @Override
88 public RouteTableId id() {
89 return id;
90 }
91
92 @Override
93 public void shutdown() {
94 routes.removeListener(listener);
95 }
96
97 @Override
98 public void destroy() {
99 shutdown();
100 routes.destroy();
101 }
102
103 @Override
104 public void update(Route route) {
105 routes.compute(route.prefix(), (prefix, set) -> {
106 if (set == null) {
107 set = new HashSet<>();
108 }
109 set.add(route);
110 return set;
111 });
112 }
113
114 @Override
115 public void remove(Route route) {
116 routes.compute(route.prefix(), (prefix, set) -> {
117 if (set != null) {
118 set.remove(route);
119 if (set.isEmpty()) {
120 return null;
121 }
122 return set;
123 }
124 return null;
125 });
126 }
127
128 @Override
129 public Collection<RouteSet> getRoutes() {
130 return routes.entrySet().stream()
131 .map(e -> new RouteSet(id, e.getKey(), e.getValue().value()))
132 .collect(Collectors.toSet());
133 }
134
135 @Override
136 public RouteSet getRoutes(IpPrefix prefix) {
137 Versioned<Set<Route>> routeSet = routes.get(prefix);
138
139 if (routeSet != null) {
140 return new RouteSet(id, prefix, routeSet.value());
141 }
142 return null;
143 }
144
145 @Override
146 public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
147 // TODO index
148 return routes.values().stream()
149 .flatMap(v -> v.value().stream())
150 .filter(r -> r.nextHop().equals(nextHop))
151 .collect(Collectors.toSet());
152 }
153
154 private class RouteTableListener
155 implements MapEventListener<IpPrefix, Set<Route>> {
156
157 private InternalRouteEvent createRouteEvent(
158 InternalRouteEvent.Type type, MapEvent<IpPrefix, Set<Route>> event) {
159 Set<Route> currentRoutes =
160 (event.newValue() == null) ? Collections.emptySet() : event.newValue().value();
161 return new InternalRouteEvent(type, new RouteSet(id, event.key(), currentRoutes));
162 }
163
164 @Override
165 public void event(MapEvent<IpPrefix, Set<Route>> event) {
166 InternalRouteEvent ire = null;
167 switch (event.type()) {
168 case INSERT:
169 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
170 break;
171 case UPDATE:
172 if (event.newValue().value().size() > event.oldValue().value().size()) {
173 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, event);
174 } else {
175 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
176 }
177 break;
178 case REMOVE:
179 ire = createRouteEvent(InternalRouteEvent.Type.ROUTE_REMOVED, event);
180 break;
181 default:
182 break;
183 }
184 if (ire != null) {
185 delegate.notify(ire);
186 }
187 }
188 }
189}