blob: 3e73d8f4255d752116737f04c5c7e069077b2bda [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 */
Thomas Vachuska4998caa2015-08-26 13:28:38 -070016package org.onosproject.store.config.impl;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070017
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070019import com.fasterxml.jackson.databind.ObjectMapper;
Jonathan Hart111b42b2015-07-14 13:28:05 -070020import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070021import com.fasterxml.jackson.databind.node.BooleanNode;
22import com.fasterxml.jackson.databind.node.DoubleNode;
Ayaka Koshibe1a002512015-09-03 13:09:23 -070023import com.fasterxml.jackson.databind.node.IntNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070024import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.LongNode;
26import com.fasterxml.jackson.databind.node.ObjectNode;
27import com.fasterxml.jackson.databind.node.ShortNode;
28import com.fasterxml.jackson.databind.node.TextNode;
29import com.google.common.collect.ImmutableSet;
30import com.google.common.collect.Maps;
Madan Jampania29c6772015-08-17 13:17:07 -070031
Thomas Vachuska96d55b12015-05-11 08:52:03 -070032import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
37import org.apache.felix.scr.annotations.Service;
38import org.onlab.util.KryoNamespace;
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070039import org.onlab.util.Tools;
Ray Milkeya4122362015-08-18 15:19:08 -070040import org.onosproject.net.config.Config;
41import org.onosproject.net.config.ConfigApplyDelegate;
42import org.onosproject.net.config.ConfigFactory;
43import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigStore;
45import org.onosproject.net.config.NetworkConfigStoreDelegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070046import org.onosproject.store.AbstractStore;
47import org.onosproject.store.serializers.KryoNamespaces;
48import org.onosproject.store.service.ConsistentMap;
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070049import org.onosproject.store.service.ConsistentMapException;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070050import org.onosproject.store.service.MapEvent;
51import org.onosproject.store.service.MapEventListener;
52import org.onosproject.store.service.Serializer;
53import org.onosproject.store.service.StorageService;
54import org.onosproject.store.service.Versioned;
55import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.LinkedHashMap;
59import java.util.Map;
60import java.util.Objects;
61import java.util.Set;
62
Ray Milkeya4122362015-08-18 15:19:08 -070063import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070064
65/**
66 * Implementation of a distributed network configuration store.
67 */
68@Component(immediate = true)
69@Service
70public class DistributedNetworkConfigStore
71 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
72 implements NetworkConfigStore {
73
Thomas Vachuska5f2cbe62015-07-30 15:10:34 -070074 private static final int MAX_BACKOFF = 10;
75
Thomas Vachuska96d55b12015-05-11 08:52:03 -070076 private final Logger log = LoggerFactory.getLogger(getClass());
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected StorageService storageService;
80
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070081 private ConsistentMap<ConfigKey, JsonNode> configs;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070082
83 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
84 private final ObjectMapper mapper = new ObjectMapper();
85 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070086 private final MapEventListener<ConfigKey, JsonNode> listener = new InternalMapListener();
Thomas Vachuska96d55b12015-05-11 08:52:03 -070087
88 @Activate
89 public void activate() {
90 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
91 .register(KryoNamespaces.API)
Jonathan Hart111b42b2015-07-14 13:28:05 -070092 .register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
Thomas Vachuska96d55b12015-05-11 08:52:03 -070093 JsonNodeFactory.class, LinkedHashMap.class,
94 TextNode.class, BooleanNode.class,
Ayaka Koshibe1a002512015-09-03 13:09:23 -070095 LongNode.class, DoubleNode.class, ShortNode.class, IntNode.class);
Thomas Vachuska96d55b12015-05-11 08:52:03 -070096
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070097 configs = storageService.<ConfigKey, JsonNode>consistentMapBuilder()
Thomas Vachuska96d55b12015-05-11 08:52:03 -070098 .withSerializer(Serializer.using(kryoBuilder.build()))
99 .withName("onos-network-configs")
Madan Jampani3d6a2f62015-08-12 07:19:07 -0700100 .withRelaxedReadConsistency()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700101 .build();
102 configs.addListener(listener);
103 log.info("Started");
104 }
105
106 @Deactivate
107 public void deactivate() {
108 configs.removeListener(listener);
109 log.info("Stopped");
110 }
111
112 @Override
113 public void addConfigFactory(ConfigFactory configFactory) {
114 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700115 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
116 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700117 }
118
119 @Override
120 public void removeConfigFactory(ConfigFactory configFactory) {
121 factoriesByConfig.remove(configFactory.configClass().getName());
Thomas Vachuskae6360222015-07-21 10:10:36 -0700122 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
123 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700124 }
125
126 @Override
127 @SuppressWarnings("unchecked")
128 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
129 return (ConfigFactory<S, C>) factoriesByConfig.get(configClass.getName());
130 }
131
132 @Override
133 @SuppressWarnings("unchecked")
134 public <S> Set<S> getSubjects(Class<S> subjectClass) {
135 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
136 configs.keySet().forEach(k -> {
137 if (subjectClass.isInstance(k.subject)) {
138 builder.add((S) k.subject);
139 }
140 });
141 return builder.build();
142 }
143
144 @Override
145 @SuppressWarnings("unchecked")
146 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
147 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
148 String cName = configClass.getName();
149 configs.keySet().forEach(k -> {
150 if (subjectClass.isInstance(k.subject) && cName.equals(k.configClass)) {
151 builder.add((S) k.subject);
152 }
153 });
154 return builder.build();
155 }
156
157 @Override
158 @SuppressWarnings("unchecked")
159 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
160 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
161 configs.keySet().forEach(k -> {
162 if (Objects.equals(subject, k.subject) && delegate != null) {
163 builder.add(factoriesByConfig.get(k.configClass).configClass());
164 }
165 });
166 return builder.build();
167 }
168
169 @Override
170 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Madan Jampania29c6772015-08-17 13:17:07 -0700171 // TODO: need to identify and address the root cause for timeouts.
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700172 Versioned<JsonNode> json = Tools.retryable(configs::get, ConsistentMapException.class, 1, MAX_BACKOFF)
173 .apply(key(subject, configClass));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700174 return json != null ? createConfig(subject, configClass, json.value()) : null;
175 }
176
177
178 @Override
179 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700180 ConfigFactory<S, C> factory = getConfigFactory(configClass);
181 Versioned<JsonNode> json = configs.computeIfAbsent(key(subject, configClass),
182 k -> factory.isList() ?
183 mapper.createArrayNode() :
184 mapper.createObjectNode());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700185 return createConfig(subject, configClass, json.value());
186 }
187
188 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700189 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700190 return createConfig(subject, configClass,
191 configs.putAndGet(key(subject, configClass), json).value());
192 }
193
194 @Override
195 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
196 configs.remove(key(subject, configClass));
197 }
198
199 /**
200 * Produces a config from the specified subject, config class and raw JSON.
201 *
202 * @param subject config subject
203 * @param configClass config class
204 * @param json raw JSON data
205 * @return config object or null of no factory found or if the specified
206 * JSON is null
207 */
208 @SuppressWarnings("unchecked")
209 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700210 JsonNode json) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700211 if (json != null) {
212 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
213 if (factory != null) {
214 C config = factory.createConfig();
215 config.init(subject, factory.configKey(), json, mapper, applyDelegate);
216 return config;
217 }
218 }
219 return null;
220 }
221
222
223 // Auxiliary delegate to receive notifications about changes applied to
224 // the network configuration - by the apps.
225 private class InternalApplyDelegate implements ConfigApplyDelegate {
226 @Override
227 public void onApply(Config config) {
228 configs.put(key(config.subject(), config.getClass()), config.node());
229 }
230 }
231
232 // Produces a key for uniquely tracking a subject config.
233 private static ConfigKey key(Object subject, Class<?> configClass) {
234 return new ConfigKey(subject, configClass);
235 }
236
237 // Auxiliary key to track subject configurations.
238 private static final class ConfigKey {
239 final Object subject;
240 final String configClass;
241
242 private ConfigKey(Object subject, Class<?> configClass) {
243 this.subject = subject;
244 this.configClass = configClass.getName();
245 }
246
247 @Override
248 public int hashCode() {
249 return Objects.hash(subject, configClass);
250 }
251
252 @Override
253 public boolean equals(Object obj) {
254 if (this == obj) {
255 return true;
256 }
257 if (obj instanceof ConfigKey) {
258 final ConfigKey other = (ConfigKey) obj;
259 return Objects.equals(this.subject, other.subject)
260 && Objects.equals(this.configClass, other.configClass);
261 }
262 return false;
263 }
264 }
265
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700266 private class InternalMapListener implements MapEventListener<ConfigKey, JsonNode> {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700267 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700268 public void event(MapEvent<ConfigKey, JsonNode> event) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700269 NetworkConfigEvent.Type type;
270 switch (event.type()) {
271 case INSERT:
272 type = CONFIG_ADDED;
273 break;
274 case UPDATE:
275 type = CONFIG_UPDATED;
276 break;
277 case REMOVE:
278 default:
279 type = CONFIG_REMOVED;
280 break;
281 }
282 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
283 if (factory != null) {
284 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
285 factory.configClass()));
286 }
287 }
288 }
289}