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