BUG-624 make netconf tcp address optional in config.ini with default value set to...
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / impl / SchemaAwareDataStoreAdapter.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.dom.broker.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import java.util.ArrayList;
13 import java.util.Comparator;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.concurrent.Future;
19
20 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
21 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
22 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
23 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
24 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
25 import org.opendaylight.controller.sal.core.api.data.DataStore;
26 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
27 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.common.RpcResult;
30 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.Node;
33 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
34 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.google.common.base.Predicate;
44 import com.google.common.collect.FluentIterable;
45 import com.google.common.collect.ImmutableSet;
46
47 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
48         DataStore, //
49         SchemaContextListener, //
50         AutoCloseable {
51
52     private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
53
54     private SchemaContext schema = null;
55     private boolean validationEnabled = false;
56     private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
57
58     @Override
59     public boolean containsConfigurationPath(InstanceIdentifier path) {
60         try {
61             getDelegateReadLock().lock();
62             return getDelegate().containsConfigurationPath(path);
63
64         } finally {
65             getDelegateReadLock().unlock();
66         }
67     }
68
69     @Override
70     public boolean containsOperationalPath(InstanceIdentifier path) {
71         try {
72             getDelegateReadLock().lock();
73             return getDelegate().containsOperationalPath(path);
74
75         } finally {
76             getDelegateReadLock().unlock();
77         }
78     }
79
80     @Override
81     public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
82         try {
83             getDelegateReadLock().lock();
84             return getDelegate().getStoredConfigurationPaths();
85
86         } finally {
87             getDelegateReadLock().unlock();
88         }
89     }
90
91     @Override
92     public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
93         try {
94             getDelegateReadLock().lock();
95             return getDelegate().getStoredOperationalPaths();
96
97         } finally {
98             getDelegateReadLock().unlock();
99         }
100     }
101
102     @Override
103     public CompositeNode readConfigurationData(InstanceIdentifier path) {
104         return reader.readConfigurationData(path);
105     }
106
107     @Override
108     public CompositeNode readOperationalData(InstanceIdentifier path) {
109         return reader.readOperationalData(path);
110     }
111
112     @Override
113     public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
114             DataModification<InstanceIdentifier, CompositeNode> modification) {
115         validateAgainstSchema(modification);
116         NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
117         cleanedUp.status = TransactionStatus.SUBMITED;
118         return retrieveDelegate().requestCommit(cleanedUp);
119     }
120
121     public boolean isValidationEnabled() {
122         return validationEnabled;
123     }
124
125     public void setValidationEnabled(boolean validationEnabled) {
126         this.validationEnabled = validationEnabled;
127     }
128
129     private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
130         if (!validationEnabled) {
131             return;
132         }
133
134         if (schema == null) {
135             LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
136             return;
137         }
138     }
139
140     @Override
141     protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
142         // NOOP
143     }
144
145     @Override
146     public void onGlobalContextUpdated(SchemaContext context) {
147         this.schema = context;
148     }
149
150     @Override
151     public void close() throws Exception {
152         this.schema = null;
153     }
154
155     protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
156             boolean config) {
157         // long startTime = System.nanoTime();
158         try {
159             DataSchemaNode node = schemaNodeFor(path);
160             return YangDataOperations.merge(node, stored, modified, config);
161         } finally {
162             // System.out.println("Merge time: " + ((System.nanoTime() -
163             // startTime) / 1000.0d));
164         }
165     }
166
167     private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
168         checkState(schema != null, "YANG Schema is not available");
169         return YangSchemaUtils.getSchemaNode(schema, path);
170     }
171
172     private NormalizedDataModification prepareMergedTransaction(
173             DataModification<InstanceIdentifier, CompositeNode> original) {
174         NormalizedDataModification normalized = new NormalizedDataModification(original);
175         LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
176                 original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
177         LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
178                 original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
179         LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
180                 original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
181
182         for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
183             normalized.deepRemoveConfigurationData(entry);
184         }
185         for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
186             normalized.deepRemoveOperationalData(entry);
187         }
188         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
189             normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
190         }
191         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
192             normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
193         }
194         return normalized;
195     }
196
197     private Iterable<InstanceIdentifier> getConfigurationSubpaths(InstanceIdentifier entry) {
198         // FIXME: This should be replaced by index
199         Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
200
201         return getChildrenPaths(entry, paths);
202
203     }
204
205     public Iterable<InstanceIdentifier> getOperationalSubpaths(InstanceIdentifier entry) {
206         // FIXME: This should be indexed
207         Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
208
209         return getChildrenPaths(entry, paths);
210     }
211
212     private static final Iterable<InstanceIdentifier> getChildrenPaths(InstanceIdentifier entry,
213             Iterable<InstanceIdentifier> paths) {
214         ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
215         for (InstanceIdentifier potential : paths) {
216             if (entry.contains(potential)) {
217                 children.add(entry);
218             }
219         }
220         return children.build();
221     }
222
223     private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
224         @Override
225         public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
226             InstanceIdentifier o1Key = o1.getKey();
227             InstanceIdentifier o2Key = o2.getKey();
228             return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
229         }
230     };
231
232     private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
233
234         @Override
235         public CompositeNode readConfigurationData(final InstanceIdentifier path) {
236             getDelegateReadLock().lock();
237             try {
238                 if (path.getPath().isEmpty()) {
239                     return null;
240                 }
241                 QName qname = null;
242                 CompositeNode original = getDelegate().readConfigurationData(path);
243                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
244                 if (original != null) {
245                     childNodes.addAll(original.getChildren());
246                     qname = original.getNodeType();
247                 } else {
248                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
249                 }
250
251                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
252                         .filter(new Predicate<InstanceIdentifier>() {
253                             @Override
254                             public boolean apply(InstanceIdentifier input) {
255                                 if (path.contains(input)) {
256                                     int nesting = input.getPath().size() - path.getPath().size();
257                                     if (nesting == 1) {
258                                         return true;
259                                     }
260                                 }
261                                 return false;
262                             }
263                         });
264                 for (InstanceIdentifier instanceIdentifier : directChildren) {
265                     childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
266                 }
267                 if (original == null && childNodes.isEmpty()) {
268                     return null;
269                 }
270
271                 return new CompositeNodeTOImpl(qname, null, childNodes);
272             } finally {
273                 getDelegateReadLock().unlock();
274             }
275         }
276
277         @Override
278         public CompositeNode readOperationalData(final InstanceIdentifier path) {
279             getDelegateReadLock().lock();
280             try {
281                 if (path.getPath().isEmpty()) {
282                     return null;
283                 }
284                 QName qname = null;
285                 CompositeNode original = getDelegate().readOperationalData(path);
286                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
287                 if (original != null) {
288                     childNodes.addAll(original.getChildren());
289                     qname = original.getNodeType();
290                 } else {
291                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
292                 }
293
294                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
295                         .filter(new Predicate<InstanceIdentifier>() {
296                             @Override
297                             public boolean apply(InstanceIdentifier input) {
298                                 if (path.contains(input)) {
299                                     int nesting = input.getPath().size() - path.getPath().size();
300                                     if (nesting == 1) {
301                                         return true;
302                                     }
303                                 }
304                                 return false;
305                             }
306                         });
307
308                 for (InstanceIdentifier instanceIdentifier : directChildren) {
309                     childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
310                 }
311                 if (original == null && childNodes.isEmpty()) {
312                     return null;
313                 }
314
315                 return new CompositeNodeTOImpl(qname, null, childNodes);
316             } finally {
317                 getDelegateReadLock().unlock();
318             }
319         }
320     }
321
322     private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
323
324         private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
325         private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
326         private final Object identifier;
327         private TransactionStatus status;
328
329         public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
330             super(getDelegate());
331             identifier = original;
332             status = TransactionStatus.NEW;
333         }
334
335         /**
336          *
337          * Ensures all subpaths are removed - this currently does slow lookup in
338          * all keys.
339          *
340          * @param entry
341          */
342         public void deepRemoveOperationalData(InstanceIdentifier entry) {
343             Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
344             removeOperationalData(entry);
345             for (InstanceIdentifier potential : paths) {
346                 removeOperationalData(potential);
347             }
348         }
349
350         public void deepRemoveConfigurationData(InstanceIdentifier entry) {
351             Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
352             removeConfigurationData(entry);
353             for (InstanceIdentifier potential : paths) {
354                 removeConfigurationData(potential);
355             }
356         }
357
358         public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
359             this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
360         }
361
362         public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
363             this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
364         }
365
366         @Override
367         public Object getIdentifier() {
368             return this.identifier;
369         }
370
371         @Override
372         public TransactionStatus getStatus() {
373             return status;
374         }
375
376         @Override
377         public Future<RpcResult<TransactionStatus>> commit() {
378             throw new UnsupportedOperationException("Commit should not be invoked on this");
379         }
380
381         @Override
382         protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
383                 CompositeNode modified) {
384             return mergeData(path, stored, modified, true);
385         }
386
387         @Override
388         protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
389                 CompositeNode modified) {
390             return mergeData(path, stored, modified, false);
391         }
392
393         private void putData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
394             if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
395                 switch (dataStoreIdentifier) {
396                 case (CONFIGURATIONAL_DATA_STORE_MARKER):
397                     this.putConfigurationData(entryKey, entryData);
398                     break;
399                 case (OPERATIONAL_DATA_STORE_MARKER):
400                     this.putOperationalData(entryKey, entryData);
401                     break;
402
403                 default:
404                     LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
405                     throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
406                 }
407             }
408         }
409
410         private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData,
411                 String dataStoreIdentifier) {
412             this.putData(entryKey, entryData, dataStoreIdentifier);
413
414             for (Node<?> child : entryData.getChildren()) {
415                 InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
416                         .toInstance();
417                 if (child instanceof CompositeNode) {
418                     DataSchemaNode subSchema = schemaNodeFor(subEntryId);
419                     CompositeNode compNode = (CompositeNode) child;
420                     InstanceIdentifier instanceId = null;
421
422                     if (subSchema instanceof ListSchemaNode) {
423                         ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
424                         Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
425                                 (CompositeNode) child);
426                         if (mapOfSubValues != null) {
427                             instanceId = InstanceIdentifier.builder(entryKey)
428                                     .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
429                         }
430                     } else if (subSchema instanceof ContainerSchemaNode) {
431                         ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
432                         instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
433                     }
434                     if (instanceId != null) {
435                         this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
436                     }
437                 }
438             }
439         }
440
441         private Map<QName, Object> getValuesFromListSchema(ListSchemaNode listSchema, CompositeNode entryData) {
442             List<QName> keyDef = listSchema.getKeyDefinition();
443             if (keyDef != null && !keyDef.isEmpty()) {
444                 Map<QName, Object> map = new HashMap<QName, Object>();
445                 for (QName key : keyDef) {
446                     List<Node<?>> data = entryData.get(key);
447                     if (data != null && !data.isEmpty()) {
448                         for (Node<?> nodeData : data) {
449                             if (nodeData instanceof SimpleNode<?>) {
450                                 map.put(key, data.get(0).getValue());
451                             }
452                         }
453                     }
454                 }
455                 return map;
456             }
457             return null;
458         }
459     }
460 }