blob: c437ecd640d755080e3d316836250cc68950d2d8 [file] [log] [blame]
Sithara Punnassery9306e6b2017-02-06 15:38:19 -08001/*
2 * Copyright 2016-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 */
16package org.onosproject.config.impl;
17
Sithara Punnassery06208792017-02-10 16:25:29 -080018import com.google.common.annotations.Beta;
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.util.KryoNamespace;
26import org.onosproject.config.DynamicConfigEvent;
27import org.onosproject.config.DynamicConfigStore;
28import org.onosproject.config.DynamicConfigStoreDelegate;
29import org.onosproject.config.FailedException;
30import org.onosproject.config.Filter;
31import org.onosproject.config.model.DataNode;
32import org.onosproject.config.model.InnerNode;
33import org.onosproject.config.model.LeafNode;
34import org.onosproject.config.model.NodeKey;
35import org.onosproject.config.model.ResourceId;
36import org.onosproject.config.model.SchemaId;
37import org.onosproject.store.AbstractStore;
38import org.onosproject.store.serializers.KryoNamespaces;
39import org.onosproject.store.service.AsyncDocumentTree;
40import org.onosproject.store.service.ConsistentMap;
41import org.onosproject.store.service.DocumentPath;
42import org.onosproject.store.service.DocumentTreeEvent;
43import org.onosproject.store.service.DocumentTreeListener;
44import org.onosproject.store.service.MapEvent;
45import org.onosproject.store.service.MapEventListener;
46import org.onosproject.store.service.Serializer;
47import org.onosproject.store.service.StorageService;
48import org.onosproject.store.service.Versioned;
49import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.util.Map;
53import java.util.concurrent.CompletableFuture;
54
55/**
56 * Implementation of the dynamic config store.
57 */
Sithara Punnassery06208792017-02-10 16:25:29 -080058@Beta
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080059@Component(immediate = true)
60@Service
61public class DistributedDynamicConfigStore
62 extends AbstractStore<DynamicConfigEvent, DynamicConfigStoreDelegate>
63 implements DynamicConfigStore {
64 private final Logger log = LoggerFactory.getLogger(getClass());
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected StorageService storageService;
67 private AsyncDocumentTree<DataNode.Type> keystore;
68 private ConsistentMap<ResourceId, LeafNode> objectStore;
69 private final DocumentTreeListener<DataNode.Type> klistener = new InternalDocTreeListener();
70 private final MapEventListener<ResourceId, LeafNode> olistener = new InternalMapListener();
71
72 @Activate
73 public void activateStore() {
74 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
75 .register(KryoNamespaces.BASIC)
76 .register(String.class)
77 .register(java.lang.Class.class)
78 .register(DataNode.Type.class)
79 .register(LeafNode.class)
80 .register(InnerNode.class)
81 .register(ResourceId.class)
82 .register(NodeKey.class)
83 .register(SchemaId.class)
84 .register(java.util.LinkedHashMap.class);
85 keystore = storageService.<DataNode.Type>documentTreeBuilder()
86 .withSerializer(Serializer.using(kryoBuilder.build()))
87 .withName("config-key-store")
88 .withRelaxedReadConsistency()
89 .buildDocumentTree();
90 objectStore = storageService.<ResourceId, LeafNode>consistentMapBuilder()
91 .withSerializer(Serializer.using(kryoBuilder.build()))
92 .withName("config-object-store")
93 .withRelaxedReadConsistency()
94 .build();
95 keystore.addListener(klistener);
96 objectStore.addListener(olistener);
97 log.info("DyanmicConfig Store Active");
98 }
99
100 @Deactivate
101 public void deactivateStore() {
102 keystore.removeListener(klistener);
103 objectStore.removeListener(olistener);
104 log.info("DyanmicConfig Store Stopped");
105 }
106
107 @Override
108 public CompletableFuture<Boolean>
109 addNode(ResourceId path, DataNode node) {
110 CompletableFuture<Boolean> eventFuture = CompletableFuture.completedFuture(false);
111 Boolean stat = false;
112 DocumentPath dpath = DocumentPath.from(path.asString());
113 log.info("STORE: dpath to parent {}", dpath);
114 if (keystore.get(dpath).join() == null) {
115 throw new FailedException("Some of the parents are not present in " +
116 "the requested path, please use a recursive create");
117 }
118 ResourceId cpath = path.builder()
119 .addBranchPointSchema(node.key().schemaId().name(),
120 node.key().schemaId().namespace()).build();
121 dpath = DocumentPath.from(cpath.asString());
122 if (keystore.get(dpath).join() != null) {
123 throw new FailedException("Requested node already present in the" +
124 " store, please use an update method");
125 }
126 stat = checkNode(cpath, node);
127 if (stat) {
128 eventFuture = CompletableFuture.completedFuture(true);
129 } else {
130 log.info("STORE: FAILED to create node @ {}", path);
131 }
132 return eventFuture;
133 }
134
135 @Override
136 public CompletableFuture<DataNode> readNode(ResourceId path, Filter filter) {
137 CompletableFuture<DataNode> eventFuture = CompletableFuture.completedFuture(null);
138 DocumentPath dpath = DocumentPath.from(path.asString());
139 DataNode.Type type;
140 type = keystore.get(dpath).join().value();
141 if (type == null) {
142 throw new FailedException("Requested node or some of the parents" +
143 "are not present in the requested path");
144 }
145 DataNode retVal = null;
146 //TODO handle single and multi instances differently
147 if ((type == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
148 (type == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
149 retVal = readLeaf(path);
150 } else {
151 int last = path.nodeKeys().size();
152 NodeKey key = path.nodeKeys().get(last - 1);
153 DataNode.Builder superBldr = new InnerNode.Builder(key.schemaId().name(),
154 key.schemaId().namespace()).type(type);
155 readInner(superBldr, path);
156 retVal = superBldr.build();
157 }
158 if (retVal != null) {
159 eventFuture = CompletableFuture.completedFuture(retVal);
160 } else {
161 log.info("STORE: FAILED to READ node @@@@");
162 }
163 return eventFuture;
164 }
165
166 @Override
167 public CompletableFuture<Boolean>
168 addRecursive(ResourceId path, DataNode node) {
169 CompletableFuture<Boolean> eventFuture = CompletableFuture.completedFuture(false);
170 Boolean stat = false;
171 DocumentPath dpath = DocumentPath.from(path.asString());
172 //TODO need to check for each parent in the path and recursively create all missing
173 /*if (keystore.get(dpath).join() == null) {
174 //recursivley craete all missing aprents
175 }*/
176 if (keystore.get(dpath).join() != null) {
177 throw new FailedException("Requested node already present " +
178 "in the store, please use an update method");
179 }
180 //TODO single instance and multi instance need to be handled differently
181 if ((node.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
182 (node.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
183 stat = addLeaf(path, (LeafNode) node);
184 } else {
185 stat = (traverseInner(path, (InnerNode) node));
186 }
187 if (stat) {
188 eventFuture = CompletableFuture.completedFuture(true);
189 } else {
190 log.info("STORE: FAILED to create node @@@@");
191 }
192 return eventFuture;
193 }
194 @Override
195 public CompletableFuture<Boolean> updateNode(ResourceId path, DataNode node) {
196 throw new FailedException("Not yet implemented");
197 }
198 @Override
199 public CompletableFuture<Boolean>
200 updateNodeRecursive(ResourceId path, DataNode node) {
201 throw new FailedException("Not yet implemented");
202 }
203 @Override
204 public CompletableFuture<Boolean>
205 replaceNode(ResourceId path, DataNode node) {
206 throw new FailedException("Not yet implemented");
207 }
208 @Override
209 public CompletableFuture<Boolean>
210 deleteNode(ResourceId path) {
211 throw new FailedException("Not yet implemented");
212 }
213 @Override
214 public CompletableFuture<Boolean>
215 deleteNodeRecursive(ResourceId path) {
216 throw new FailedException("Not yet implemented");
217 }
218
219 private Boolean addLeaf(ResourceId path, LeafNode node) {
220 objectStore.put(path, node);
221 return (keystore.create(DocumentPath.from(path.asString()), node.type()).join());
222 }
223
224 private Boolean addKey(ResourceId path, DataNode.Type type) {
225 return (keystore.create(DocumentPath.from(path.asString()), type).join());
226 }
227
228 private Boolean checkNode(ResourceId path, DataNode node) {
229 //TODO single instance and multi instance need to be handled differently
230 if ((node.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
231 (node.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
232 return (addLeaf(path, (LeafNode) node));
233 } else if ((node.type() == DataNode.Type.SINGLE_INSTANCE_NODE) ||
234 (node.type() == DataNode.Type.MULTI_INSTANCE_NODE)) {
235 addKey(path, node.type());
236 return (traverseInner(path, (InnerNode) node));
237 } else {
238 throw new FailedException("Node type should either be LEAF or INNERNODE");
239 }
240 }
241
242 private LeafNode readLeaf(ResourceId path) {
243 return objectStore.get(path).value();
244 }
245
246 private Boolean traverseInner(ResourceId path, InnerNode node) {
247 addKey(path, node.type());
248 Map<NodeKey, DataNode> entries = node.childNodes();
249 if (entries.size() == 0) {
250 throw new FailedException("Inner node cannot have empty children map");
251 }
252 entries.forEach((k, v) -> {
253 ResourceId tempPath;
254 try {
255 tempPath = path.copyBuilder()
256 .addBranchPointSchema(k.schemaId().name(),
257 k.schemaId().namespace())
258 .build();
259 } catch (CloneNotSupportedException e) {
260 throw new FailedException("ResourceId could not be cloned@@@@");
261 }
262 //TODO single instance and multi instance need to be handled differently
263 if ((v.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
264 (v.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
265 addLeaf(tempPath, (LeafNode) v);
266 } else if ((v.type() == DataNode.Type.SINGLE_INSTANCE_NODE) ||
267 (v.type() == DataNode.Type.MULTI_INSTANCE_NODE)) {
268 traverseInner(tempPath, (InnerNode) v);
269 } else {
270 throw new FailedException("Node type should either be LEAF or INNERNODE");
271 }
272 });
273 return true;
274 }
275
276 private void readInner(DataNode.Builder superBldr, ResourceId path) {
277 Map<String, Versioned<DataNode.Type>> entries = keystore.getChildren(
278 DocumentPath.from(path.asString())).join();
279 if (entries.size() == 0) {
280 throw new FailedException("Inner node cannot have empty children map");
281 }
282 entries.forEach((k, v) -> {
283 ResourceId tempPath;
284 String[] names = k.split("#");
285 String name = names[0];
286 String nmSpc = names[1];
287 DataNode.Type type = v.value();
288 try {
289 tempPath = path.copyBuilder()
290 .addBranchPointSchema(name, nmSpc)
291 .build();
292 } catch (CloneNotSupportedException e) {
293 throw new FailedException("ResourceId could not be cloned@@@@");
294 }
295 //TODO single instance and multi instance need to be handled differently
296 if ((type == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
297 (type == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
298 superBldr.createChildBuilder(name, nmSpc, readLeaf(tempPath))
299 .type(type)
300 .exitNode();
301 } else if ((type == DataNode.Type.SINGLE_INSTANCE_NODE) ||
302 (type == DataNode.Type.MULTI_INSTANCE_NODE)) {
303 DataNode.Builder tempBldr = superBldr.createChildBuilder(name, nmSpc)
304 .type(type);
305 readInner(tempBldr, tempPath);
306 } else {
307 throw new FailedException("Node type should either be LEAF or INNERNODE");
308 }
309 });
310 superBldr.exitNode();
311 }
312
313 public class InternalDocTreeListener implements DocumentTreeListener<DataNode.Type> {
314 @Override
315 public void event(DocumentTreeEvent<DataNode.Type> event) {
316 DynamicConfigEvent.Type type;
317 DataNode node;
318 ResourceId path;
319 switch (event.type()) {
320 case CREATED:
321 log.info("key created in store");
322 break;
323 case UPDATED:
324 log.info("key updated in store");
325 break;
326 case DELETED:
327 log.info("key deleted in store");
328 break;
329
330 default:
331 }
332 //notify
333 }
334 }
335
336 public class InternalMapListener implements MapEventListener<ResourceId, LeafNode> {
337 @Override
338 public void event(MapEvent<ResourceId, LeafNode> event) {
339 switch (event.type()) {
340 case INSERT:
341 log.info("OBJECT created in store");
342 break;
343 case UPDATE:
344 log.info("OBJECT updated in store");
345 break;
346 case REMOVE:
347 default:
348 log.info("OBJECT removed in store");
349 break;
350 }
351 //notify
352 }
353 }
354}