blob: 741ae97f4b51f4defc97ff345282a6f2dbcf0dc3 [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;
Sithara Punnassery4b091dc2017-03-02 17:22:40 -080031import org.onosproject.config.ResourceIdParser;
32import org.onosproject.store.service.IllegalDocumentModificationException;
33import org.onosproject.store.service.NoSuchDocumentPathException;
34import org.onosproject.yang.model.DataNode;
35import org.onosproject.yang.model.InnerNode;
36import org.onosproject.yang.model.LeafNode;
37import org.onosproject.yang.model.NodeKey;
38import org.onosproject.yang.model.ResourceId;
39import org.onosproject.yang.model.SchemaId;
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080040import org.onosproject.store.AbstractStore;
41import org.onosproject.store.serializers.KryoNamespaces;
42import org.onosproject.store.service.AsyncDocumentTree;
43import org.onosproject.store.service.ConsistentMap;
44import org.onosproject.store.service.DocumentPath;
45import org.onosproject.store.service.DocumentTreeEvent;
46import org.onosproject.store.service.DocumentTreeListener;
47import org.onosproject.store.service.MapEvent;
48import org.onosproject.store.service.MapEventListener;
49import org.onosproject.store.service.Serializer;
50import org.onosproject.store.service.StorageService;
51import org.onosproject.store.service.Versioned;
52import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
55import java.util.Map;
56import java.util.concurrent.CompletableFuture;
Sithara Punnassery4b091dc2017-03-02 17:22:40 -080057import java.util.concurrent.ExecutionException;
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080058
59/**
60 * Implementation of the dynamic config store.
61 */
Sithara Punnassery06208792017-02-10 16:25:29 -080062@Beta
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080063@Component(immediate = true)
64@Service
65public class DistributedDynamicConfigStore
66 extends AbstractStore<DynamicConfigEvent, DynamicConfigStoreDelegate>
67 implements DynamicConfigStore {
68 private final Logger log = LoggerFactory.getLogger(getClass());
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected StorageService storageService;
71 private AsyncDocumentTree<DataNode.Type> keystore;
Sithara Punnassery4b091dc2017-03-02 17:22:40 -080072 private ConsistentMap<String, LeafNode> objectStore;
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080073 private final DocumentTreeListener<DataNode.Type> klistener = new InternalDocTreeListener();
Sithara Punnassery4b091dc2017-03-02 17:22:40 -080074 private final MapEventListener<String, LeafNode> olistener = new InternalMapListener();
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080075
76 @Activate
77 public void activateStore() {
78 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
79 .register(KryoNamespaces.BASIC)
80 .register(String.class)
81 .register(java.lang.Class.class)
82 .register(DataNode.Type.class)
83 .register(LeafNode.class)
84 .register(InnerNode.class)
85 .register(ResourceId.class)
86 .register(NodeKey.class)
87 .register(SchemaId.class)
88 .register(java.util.LinkedHashMap.class);
89 keystore = storageService.<DataNode.Type>documentTreeBuilder()
90 .withSerializer(Serializer.using(kryoBuilder.build()))
91 .withName("config-key-store")
92 .withRelaxedReadConsistency()
93 .buildDocumentTree();
Sithara Punnassery4b091dc2017-03-02 17:22:40 -080094 objectStore = storageService.<String, LeafNode>consistentMapBuilder()
Sithara Punnassery9306e6b2017-02-06 15:38:19 -080095 .withSerializer(Serializer.using(kryoBuilder.build()))
96 .withName("config-object-store")
97 .withRelaxedReadConsistency()
98 .build();
99 keystore.addListener(klistener);
100 objectStore.addListener(olistener);
101 log.info("DyanmicConfig Store Active");
102 }
103
104 @Deactivate
105 public void deactivateStore() {
106 keystore.removeListener(klistener);
107 objectStore.removeListener(olistener);
108 log.info("DyanmicConfig Store Stopped");
109 }
110
111 @Override
112 public CompletableFuture<Boolean>
113 addNode(ResourceId path, DataNode node) {
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800114 throw new FailedException("Not yet implemented");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800115 }
116
117 @Override
118 public CompletableFuture<DataNode> readNode(ResourceId path, Filter filter) {
119 CompletableFuture<DataNode> eventFuture = CompletableFuture.completedFuture(null);
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800120 DocumentPath dpath = DocumentPath.from(ResourceIdParser.asString(path));
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800121 DataNode.Type type;
122 type = keystore.get(dpath).join().value();
123 if (type == null) {
124 throw new FailedException("Requested node or some of the parents" +
125 "are not present in the requested path");
126 }
127 DataNode retVal = null;
128 //TODO handle single and multi instances differently
129 if ((type == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
130 (type == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
131 retVal = readLeaf(path);
132 } else {
133 int last = path.nodeKeys().size();
134 NodeKey key = path.nodeKeys().get(last - 1);
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800135 DataNode.Builder superBldr = InnerNode.builder(key.schemaId().name(),
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800136 key.schemaId().namespace()).type(type);
137 readInner(superBldr, path);
138 retVal = superBldr.build();
139 }
140 if (retVal != null) {
141 eventFuture = CompletableFuture.completedFuture(retVal);
142 } else {
143 log.info("STORE: FAILED to READ node @@@@");
144 }
145 return eventFuture;
146 }
147
148 @Override
149 public CompletableFuture<Boolean>
150 addRecursive(ResourceId path, DataNode node) {
151 CompletableFuture<Boolean> eventFuture = CompletableFuture.completedFuture(false);
152 Boolean stat = false;
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800153 DocumentPath dpath = DocumentPath.from(ResourceIdParser.asString(path));
154 if (keystore.get(dpath).join() == null) {
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800155 //recursivley craete all missing aprents
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800156 }
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800157 if (keystore.get(dpath).join() != null) {
158 throw new FailedException("Requested node already present " +
159 "in the store, please use an update method");
160 }
161 //TODO single instance and multi instance need to be handled differently
162 if ((node.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
163 (node.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
164 stat = addLeaf(path, (LeafNode) node);
165 } else {
166 stat = (traverseInner(path, (InnerNode) node));
167 }
168 if (stat) {
169 eventFuture = CompletableFuture.completedFuture(true);
170 } else {
171 log.info("STORE: FAILED to create node @@@@");
172 }
173 return eventFuture;
174 }
175 @Override
176 public CompletableFuture<Boolean> updateNode(ResourceId path, DataNode node) {
177 throw new FailedException("Not yet implemented");
178 }
179 @Override
180 public CompletableFuture<Boolean>
181 updateNodeRecursive(ResourceId path, DataNode node) {
182 throw new FailedException("Not yet implemented");
183 }
184 @Override
185 public CompletableFuture<Boolean>
186 replaceNode(ResourceId path, DataNode node) {
187 throw new FailedException("Not yet implemented");
188 }
189 @Override
190 public CompletableFuture<Boolean>
191 deleteNode(ResourceId path) {
192 throw new FailedException("Not yet implemented");
193 }
194 @Override
195 public CompletableFuture<Boolean>
196 deleteNodeRecursive(ResourceId path) {
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800197 String spath = ResourceIdParser.asString(path);
198 DocumentPath dpath = DocumentPath.from(spath);
199 DataNode.Type type = null;
200 CompletableFuture<Versioned<DataNode.Type>> vtype = keystore.removeNode(dpath);
201 type = completeVersioned(vtype);
202 if (type == null) {
203 throw new FailedException("node delete failed");
204 }
205 Versioned<LeafNode> res = objectStore.remove(spath);
206 if (res == null) {
207 return CompletableFuture.completedFuture(false);
208 } else {
209 return CompletableFuture.completedFuture(true);
210 }
211
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800212 }
213
214 private Boolean addLeaf(ResourceId path, LeafNode node) {
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800215 objectStore.put(ResourceIdParser.asString(path), node);
216 return (keystore.create(DocumentPath.from(ResourceIdParser.asString(path)), node.type()).join());
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800217 }
218
219 private Boolean addKey(ResourceId path, DataNode.Type type) {
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800220 return (keystore.create(DocumentPath.from(ResourceIdParser.asString(path)), type).join());
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800221 }
222
223 private Boolean checkNode(ResourceId path, DataNode node) {
224 //TODO single instance and multi instance need to be handled differently
225 if ((node.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
226 (node.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
227 return (addLeaf(path, (LeafNode) node));
228 } else if ((node.type() == DataNode.Type.SINGLE_INSTANCE_NODE) ||
229 (node.type() == DataNode.Type.MULTI_INSTANCE_NODE)) {
230 addKey(path, node.type());
231 return (traverseInner(path, (InnerNode) node));
232 } else {
233 throw new FailedException("Node type should either be LEAF or INNERNODE");
234 }
235 }
236
237 private LeafNode readLeaf(ResourceId path) {
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800238 return objectStore.get(ResourceIdParser.asString(path)).value();
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800239 }
240
241 private Boolean traverseInner(ResourceId path, InnerNode node) {
242 addKey(path, node.type());
243 Map<NodeKey, DataNode> entries = node.childNodes();
244 if (entries.size() == 0) {
245 throw new FailedException("Inner node cannot have empty children map");
246 }
247 entries.forEach((k, v) -> {
248 ResourceId tempPath;
249 try {
250 tempPath = path.copyBuilder()
251 .addBranchPointSchema(k.schemaId().name(),
252 k.schemaId().namespace())
253 .build();
254 } catch (CloneNotSupportedException e) {
255 throw new FailedException("ResourceId could not be cloned@@@@");
256 }
257 //TODO single instance and multi instance need to be handled differently
258 if ((v.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
259 (v.type() == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
260 addLeaf(tempPath, (LeafNode) v);
261 } else if ((v.type() == DataNode.Type.SINGLE_INSTANCE_NODE) ||
262 (v.type() == DataNode.Type.MULTI_INSTANCE_NODE)) {
263 traverseInner(tempPath, (InnerNode) v);
264 } else {
265 throw new FailedException("Node type should either be LEAF or INNERNODE");
266 }
267 });
268 return true;
269 }
270
271 private void readInner(DataNode.Builder superBldr, ResourceId path) {
272 Map<String, Versioned<DataNode.Type>> entries = keystore.getChildren(
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800273 DocumentPath.from(ResourceIdParser.asString(path))).join();
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800274 if (entries.size() == 0) {
275 throw new FailedException("Inner node cannot have empty children map");
276 }
277 entries.forEach((k, v) -> {
278 ResourceId tempPath;
279 String[] names = k.split("#");
280 String name = names[0];
281 String nmSpc = names[1];
282 DataNode.Type type = v.value();
283 try {
284 tempPath = path.copyBuilder()
285 .addBranchPointSchema(name, nmSpc)
286 .build();
287 } catch (CloneNotSupportedException e) {
288 throw new FailedException("ResourceId could not be cloned@@@@");
289 }
290 //TODO single instance and multi instance need to be handled differently
291 if ((type == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) ||
292 (type == DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE)) {
293 superBldr.createChildBuilder(name, nmSpc, readLeaf(tempPath))
294 .type(type)
295 .exitNode();
296 } else if ((type == DataNode.Type.SINGLE_INSTANCE_NODE) ||
297 (type == DataNode.Type.MULTI_INSTANCE_NODE)) {
298 DataNode.Builder tempBldr = superBldr.createChildBuilder(name, nmSpc)
299 .type(type);
300 readInner(tempBldr, tempPath);
301 } else {
302 throw new FailedException("Node type should either be LEAF or INNERNODE");
303 }
304 });
305 superBldr.exitNode();
306 }
307
308 public class InternalDocTreeListener implements DocumentTreeListener<DataNode.Type> {
309 @Override
310 public void event(DocumentTreeEvent<DataNode.Type> event) {
311 DynamicConfigEvent.Type type;
312 DataNode node;
313 ResourceId path;
314 switch (event.type()) {
315 case CREATED:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800316 log.info("NODE created in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800317 break;
318 case UPDATED:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800319 log.info("NODE updated in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800320 break;
321 case DELETED:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800322 log.info("NODE deleted in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800323 break;
324
325 default:
326 }
327 //notify
328 }
329 }
330
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800331 public class InternalMapListener implements MapEventListener<String, LeafNode> {
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800332 @Override
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800333 public void event(MapEvent<String, LeafNode> event) {
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800334 switch (event.type()) {
335 case INSERT:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800336 //log.info("NODE created in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800337 break;
338 case UPDATE:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800339 //log.info("NODE updated in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800340 break;
341 case REMOVE:
342 default:
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800343 //log.info("NODE removed in store");
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800344 break;
345 }
346 //notify
347 }
348 }
Sithara Punnassery4b091dc2017-03-02 17:22:40 -0800349
350 private <T> T complete(CompletableFuture<T> future) {
351 try {
352 return future.get();
353 } catch (InterruptedException e) {
354 Thread.currentThread().interrupt();
355 throw new FailedException(e.getCause().getMessage());
356 } catch (ExecutionException e) {
357 if (e.getCause() instanceof IllegalDocumentModificationException) {
358 throw new FailedException("Node or parent doesnot exist or is root or is not a Leaf Node");
359 } else if (e.getCause() instanceof NoSuchDocumentPathException) {
360 throw new FailedException("Resource id does not exist");
361 } else {
362 throw new FailedException("Datastore operation failed");
363 }
364 }
365 }
366
367 private <T> T completeVersioned(CompletableFuture<Versioned<T>> future) {
368 try {
369 return future.get().value();
370 } catch (InterruptedException e) {
371 Thread.currentThread().interrupt();
372 throw new FailedException(e.getCause().getMessage());
373 } catch (ExecutionException e) {
374 if (e.getCause() instanceof IllegalDocumentModificationException) {
375 throw new FailedException("Node or parent does not exist or is root or is not a Leaf Node");
376 } else if (e.getCause() instanceof NoSuchDocumentPathException) {
377 throw new FailedException("Resource id does not exist");
378 } else {
379 throw new FailedException("Datastore operation failed");
380 }
381 }
382 }
Sithara Punnassery9306e6b2017-02-06 15:38:19 -0800383}