blob: 6e0c094e4d562738b3308cd3b78c4f9927454b6d [file] [log] [blame]
Thomas Vachuska96d55b12015-05-11 08:52:03 -07001/*
2 * Copyright 2015 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 */
16package org.onosproject.incubator.store.config.impl;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
Jonathan Hart111b42b2015-07-14 13:28:05 -070019import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070020import com.fasterxml.jackson.databind.node.BooleanNode;
21import com.fasterxml.jackson.databind.node.DoubleNode;
22import com.fasterxml.jackson.databind.node.JsonNodeFactory;
23import com.fasterxml.jackson.databind.node.LongNode;
24import com.fasterxml.jackson.databind.node.ObjectNode;
25import com.fasterxml.jackson.databind.node.ShortNode;
26import com.fasterxml.jackson.databind.node.TextNode;
27import com.google.common.collect.ImmutableSet;
28import com.google.common.collect.Maps;
Madan Jampania29c6772015-08-17 13:17:07 -070029
Thomas Vachuska96d55b12015-05-11 08:52:03 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
33import org.apache.felix.scr.annotations.Reference;
34import org.apache.felix.scr.annotations.ReferenceCardinality;
35import org.apache.felix.scr.annotations.Service;
36import org.onlab.util.KryoNamespace;
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070037import org.onlab.util.Tools;
Ray Milkeya4122362015-08-18 15:19:08 -070038import org.onosproject.net.config.Config;
39import org.onosproject.net.config.ConfigApplyDelegate;
40import org.onosproject.net.config.ConfigFactory;
41import org.onosproject.net.config.NetworkConfigEvent;
42import org.onosproject.net.config.NetworkConfigStore;
43import org.onosproject.net.config.NetworkConfigStoreDelegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070044import org.onosproject.store.AbstractStore;
45import org.onosproject.store.serializers.KryoNamespaces;
46import org.onosproject.store.service.ConsistentMap;
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070047import org.onosproject.store.service.ConsistentMapException;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070048import org.onosproject.store.service.MapEvent;
49import org.onosproject.store.service.MapEventListener;
50import org.onosproject.store.service.Serializer;
51import org.onosproject.store.service.StorageService;
52import org.onosproject.store.service.Versioned;
53import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.util.LinkedHashMap;
57import java.util.Map;
58import java.util.Objects;
59import java.util.Set;
60
Ray Milkeya4122362015-08-18 15:19:08 -070061import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070062
63/**
64 * Implementation of a distributed network configuration store.
65 */
66@Component(immediate = true)
67@Service
68public class DistributedNetworkConfigStore
69 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
70 implements NetworkConfigStore {
71
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070072 private static final int MAX_BACKOFF = 10;
73
Thomas Vachuska96d55b12015-05-11 08:52:03 -070074 private final Logger log = LoggerFactory.getLogger(getClass());
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected StorageService storageService;
78
79 private ConsistentMap<ConfigKey, ObjectNode> configs;
80
81 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
82 private final ObjectMapper mapper = new ObjectMapper();
83 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
84 private final MapEventListener<ConfigKey, ObjectNode> listener = new InternalMapListener();
85
86 @Activate
87 public void activate() {
88 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
89 .register(KryoNamespaces.API)
Jonathan Hart111b42b2015-07-14 13:28:05 -070090 .register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
Thomas Vachuska96d55b12015-05-11 08:52:03 -070091 JsonNodeFactory.class, LinkedHashMap.class,
92 TextNode.class, BooleanNode.class,
93 LongNode.class, DoubleNode.class, ShortNode.class);
94
95 configs = storageService.<ConfigKey, ObjectNode>consistentMapBuilder()
96 .withSerializer(Serializer.using(kryoBuilder.build()))
97 .withName("onos-network-configs")
Madan Jampani3d6a2f62015-08-12 07:19:07 -070098 .withRelaxedReadConsistency()
Thomas Vachuska96d55b12015-05-11 08:52:03 -070099 .build();
100 configs.addListener(listener);
101 log.info("Started");
102 }
103
104 @Deactivate
105 public void deactivate() {
106 configs.removeListener(listener);
107 log.info("Stopped");
108 }
109
110 @Override
111 public void addConfigFactory(ConfigFactory configFactory) {
112 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700113 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
114 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700115 }
116
117 @Override
118 public void removeConfigFactory(ConfigFactory configFactory) {
119 factoriesByConfig.remove(configFactory.configClass().getName());
Thomas Vachuskae6360222015-07-21 10:10:36 -0700120 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
121 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700122 }
123
124 @Override
125 @SuppressWarnings("unchecked")
126 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
127 return (ConfigFactory<S, C>) factoriesByConfig.get(configClass.getName());
128 }
129
130 @Override
131 @SuppressWarnings("unchecked")
132 public <S> Set<S> getSubjects(Class<S> subjectClass) {
133 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
134 configs.keySet().forEach(k -> {
135 if (subjectClass.isInstance(k.subject)) {
136 builder.add((S) k.subject);
137 }
138 });
139 return builder.build();
140 }
141
142 @Override
143 @SuppressWarnings("unchecked")
144 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
145 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
146 String cName = configClass.getName();
147 configs.keySet().forEach(k -> {
148 if (subjectClass.isInstance(k.subject) && cName.equals(k.configClass)) {
149 builder.add((S) k.subject);
150 }
151 });
152 return builder.build();
153 }
154
155 @Override
156 @SuppressWarnings("unchecked")
157 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
158 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
159 configs.keySet().forEach(k -> {
160 if (Objects.equals(subject, k.subject) && delegate != null) {
161 builder.add(factoriesByConfig.get(k.configClass).configClass());
162 }
163 });
164 return builder.build();
165 }
166
167 @Override
168 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Madan Jampania29c6772015-08-17 13:17:07 -0700169 // TODO: need to identify and address the root cause for timeouts.
170 Versioned<ObjectNode> json = Tools.retryable(configs::get, ConsistentMapException.class, 1, MAX_BACKOFF)
171 .apply(key(subject, configClass));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700172 return json != null ? createConfig(subject, configClass, json.value()) : null;
173 }
174
175
176 @Override
177 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
178 Versioned<ObjectNode> json = configs.computeIfAbsent(key(subject, configClass),
179 k -> mapper.createObjectNode());
180 return createConfig(subject, configClass, json.value());
181 }
182
183 @Override
184 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
185 return createConfig(subject, configClass,
186 configs.putAndGet(key(subject, configClass), json).value());
187 }
188
189 @Override
190 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
191 configs.remove(key(subject, configClass));
192 }
193
194 /**
195 * Produces a config from the specified subject, config class and raw JSON.
196 *
197 * @param subject config subject
198 * @param configClass config class
199 * @param json raw JSON data
200 * @return config object or null of no factory found or if the specified
201 * JSON is null
202 */
203 @SuppressWarnings("unchecked")
204 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
205 ObjectNode json) {
206 if (json != null) {
207 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
208 if (factory != null) {
209 C config = factory.createConfig();
210 config.init(subject, factory.configKey(), json, mapper, applyDelegate);
211 return config;
212 }
213 }
214 return null;
215 }
216
217
218 // Auxiliary delegate to receive notifications about changes applied to
219 // the network configuration - by the apps.
220 private class InternalApplyDelegate implements ConfigApplyDelegate {
221 @Override
222 public void onApply(Config config) {
223 configs.put(key(config.subject(), config.getClass()), config.node());
224 }
225 }
226
227 // Produces a key for uniquely tracking a subject config.
228 private static ConfigKey key(Object subject, Class<?> configClass) {
229 return new ConfigKey(subject, configClass);
230 }
231
232 // Auxiliary key to track subject configurations.
233 private static final class ConfigKey {
234 final Object subject;
235 final String configClass;
236
237 private ConfigKey(Object subject, Class<?> configClass) {
238 this.subject = subject;
239 this.configClass = configClass.getName();
240 }
241
242 @Override
243 public int hashCode() {
244 return Objects.hash(subject, configClass);
245 }
246
247 @Override
248 public boolean equals(Object obj) {
249 if (this == obj) {
250 return true;
251 }
252 if (obj instanceof ConfigKey) {
253 final ConfigKey other = (ConfigKey) obj;
254 return Objects.equals(this.subject, other.subject)
255 && Objects.equals(this.configClass, other.configClass);
256 }
257 return false;
258 }
259 }
260
261 private class InternalMapListener implements MapEventListener<ConfigKey, ObjectNode> {
262 @Override
263 public void event(MapEvent<ConfigKey, ObjectNode> event) {
264 NetworkConfigEvent.Type type;
265 switch (event.type()) {
266 case INSERT:
267 type = CONFIG_ADDED;
268 break;
269 case UPDATE:
270 type = CONFIG_UPDATED;
271 break;
272 case REMOVE:
273 default:
274 type = CONFIG_REMOVED;
275 break;
276 }
277 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
278 if (factory != null) {
279 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
280 factory.configClass()));
281 }
282 }
283 }
284}