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