4e2a96a0c919f4b7d4dcd31272ef5d7f33edbc4a
[yangtools.git] / docs / src / main / asciidoc / developer / introduction.adoc
1 = Developer Guide
2 :rfc6020: https://tools.ietf.org/html/rfc6020
3 :lhotka-yang-json: https://tools.ietf.org/html/draft-lhotka-netmod-yang-json-01
4
5 == Overview
6 YANG Tools is set of libraries and tooling providing support for use {rfc6020}[YANG] for Java (or other JVM-based language) projects and applications.
7
8 YANG Tools provides following features in OpenDaylight:
9
10 - parsing of YANG sources and
11 semantic inference of relationship across YANG models as defined in
12 {rfc6020}[RFC6020]
13 - representation of YANG-modeled data in Java
14 ** *Normalized Node* representation - DOM-like tree model, which uses conceptual
15   meta-model more tailored to YANG and OpenDaylight use-cases than a standard XML
16   DOM model allows for.
17 - serialization / deserialization of YANG-modeled data driven by YANG
18 models
19 ** XML - as defined in {rfc6020}[RFC6020]
20 ** JSON - as defined in {rfc6020}[draft-lhotka-netmod-yang-json-01]
21 ** support for third-party generators processing YANG models.
22
23 === Architecture
24 YANG Tools project consists of following logical subsystems:
25
26 - *Commons* - Set of general purpose code, which is not specific to YANG, but
27   is also useful outside YANG Tools implementation.
28 - *YANG Model and Parser* - YANG semantic model and lexical and semantic parser
29   of YANG models, which creates in-memory cross-referenced represenation of
30   YANG models, which is used by other components to determine their behaviour
31   based on the model.
32 - *YANG Data* - Definition of Normalized Node APIs and Data Tree APIs, reference
33   implementation of these APIs and implementation of XML and JSON codecs for
34   Normalized Nodes.
35 - *YANG Maven Plugin* - Maven plugin which integrates YANG parser into Maven
36   build lifecycle and provides code-generation framework for components, which
37   wants to generate code or other artefacts based on YANG model.
38
39 === Concepts
40 Project defines base concepts and helper classes which are project-agnostic and could be used outside of YANG Tools project scope. 
41
42 === Components
43
44 - yang-common
45 - yang-data-api
46 - yang-data-codec-gson
47 - yang-data-impl
48 - yang-data-jaxen
49 - yang-data-transform
50 - yang-data-util
51 - yang-maven-plugin
52 - yang-maven-plugin-it
53 - yang-maven-plugin-spi
54 - yang-model-api
55 - yang-model-export
56 - yang-model-util
57 - yang-parser-api
58 - yang-parser-impl
59
60 ==== YANG Model API
61 Class diagram of yang model API
62
63 image:models/yang-model-api.png[]
64
65 ==== YANG Parser
66
67 Yang Statement Parser works on the idea of statement concepts as defined in RFC6020, section 6.3. We come up here with basic ModelStatement and StatementDefinition, following RFC6020 idea of having sequence of statements, where
68 every statement contains keyword and zero or one argument. ModelStatement is extended by DeclaredStatement (as it comes from source, e.g. YANG source)
69 and EffectiveStatement, which contains other substatements and tends to represent result of semantic processing of other statements (uses, augment for YANG). 
70 IdentifierNamespace represents common superclass for YANG model namespaces.
71
72 Input of the Yang Statement Parser is a collection of StatementStreamSource objects.
73 StatementStreamSource interface is used for inference of effective model
74 and is required to emit its statements using supplied StatementWriter.
75 Each source (e.g. YANG source) has to be processed in three steps
76 in order to emit different statements for each step.
77 This package provides support for various namespaces used across statement parser
78 in order to map relations during declaration phase process.
79
80 Currently, there are two implementations of StatementStreamSource in Yangtools:
81
82  - YangStatementSourceImpl - intended for yang sources
83  - YinStatementSourceImpl - intended for yin sources
84
85 ==== YANG Data API
86 Class diagram of yang data API
87
88 image:models/yang-data-api.png[]
89
90 ==== YANG Data Codecs
91
92 ==== YANG Maven Plugin
93 Maven plugin which integrates YANG parser into Maven
94   build lifecycle and provides code-generation framework for components, which
95   wants to generate code or other artefacts based on YANG model.
96
97 == How to / Tutorials
98
99 === Working with YANG Model
100 First thing you need to do if you want to work with YANG models is to instantiate a SchemaContext object. This object type describes one or more parsed YANG modules. 
101
102 In order to create it you need to utilize YANG statement parser which takes one or more StatementStreamSource objects as input and then produces the SchemaContext object. 
103
104 StatementStreamSource object contains the source file information. It has two implementations, one for YANG sources - YangStatementSourceImpl, and one for YIN sources - YinStatementSourceImpl. 
105
106 Here is an example of creating StatementStreamSource objects for YANG files, providing them to the YANG statement parser and building the SchemaContext:
107
108 [source,java]
109 ----
110 StatementStreamSource yangModuleSource = new YangStatementSourceImpl("/example.yang", false);
111 StatementStreamSource yangModuleSource2 = new YangStatementSourceImpl("/example2.yang", false);
112
113 CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
114 reactor.addSources(yangModuleSource, yangModuleSource2);
115
116 SchemaContext schemaContext = reactor.buildEffective();
117 ----
118
119 First, StatementStreamSource objects with two constructor arguments should be instantiated: path to the yang source file (which is a regular String object) and a boolean which determines if the path is absolute or relative. 
120
121 Next comes the initiation of new yang parsing cycle - which is represented by  CrossSourceStatementReactor.BuildAction object. You can get it by calling method newBuild() on CrossSourceStatementReactor object (RFC6020_REACTOR) in YangInferencePipeline class. 
122
123 Then you should feed yang sources to it by calling method addSources() that takes one or more StatementStreamSource objects as arguments.
124
125 Finally you call the method buildEffective() on the reactor object which returns EffectiveSchemaContext (that is a concrete implementation of SchemaContext). Now you are ready to work with contents of the added yang sources.
126
127 Let us explain how to work with models contained in the newly created SchemaContext. If you want to get all the modules in the schemaContext, you have to call method getModules() which returns a Set of modules. If you want to get all the data definitions in schemaContext, you need to call method getDataDefinitions, etc.
128
129 [source, java]
130 Set<Module> modules = schemaContext.getModules();
131 Set<DataSchemaNodes> dataSchemaNodes = schemaContext.getDataDefinitions();
132
133 Usually you want to access specific modules. Getting a concrete module from SchemaContext is a matter of calling one of these methods: 
134
135 * findModuleByName(),
136 * findModuleByNamespace(),
137 * findModuleByNamespaceAndRevision().
138
139 In the first case, you need to provide module name as it is defined in the yang source file and module revision date if it specified in the yang source file (if it is not defined, you can just pass a null value). In order to provide the revision date in proper format, you can use a utility class named SimpleDateFormatUtil.
140
141 [source, java]
142 Module exampleModule = schemaContext.findModuleByName("example-module", null);
143 // or
144 Date revisionDate = SimpleDateFormatUtil.getRevisionFormat().parse("2015-09-02");
145 Module exampleModule = schemaContext.findModuleByName("example-module", revisionDate);
146
147 In the second case, you have to provide module namespace in form of an URI object.
148 [source, java]
149 Module exampleModule = schema.findModuleByNamespace(new URI("opendaylight.org/example-module"));
150
151 In the third case, you provide both module namespace and revision date as arguments.
152
153 Once you have a Module object, you can access its contents as they are defined in YANG Model API.
154 One way to do this is to use method like getIdentities() or getRpcs() which will give you a Set of objects. Otherwise you can access a DataSchemaNode directly via the method getDataChildByName() which takes a QName object as its only argument. Here are a few examples.
155
156 [source, java]
157 ----
158 Set<AugmentationSchema> augmentationSchemas = exampleModule.getAugmentations();
159 Set<ModuleImport> moduleImports = exampleModule.getImports();
160
161 ChoiceSchemaNode choiceSchemaNode = (ChoiceSchemaNode) exampleModule.getDataChildByName(QName.create(exampleModule.getQNameModule(), "example-choice"));
162
163 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) exampleModule.getDataChildByName(QName.create(exampleModule.getQNameModule(), "example-container"));
164 ----
165
166 === Working with YANG Data
167 If you want to work with YANG Data you are going to need NormalizedNode objects that are specified in the YANG Data API. NormalizedNode is an interface at the top of the YANG Data hierarchy. It is extended through sub-interfaces which define the behaviour of specific NormalizedNode types like AnyXmlNode, ChoiceNode, LeafNode, ContainerNode, etc. Concrete implemenations of these interfaces are defined in yang-data-impl module. Once you have one or more NormalizedNode instances, you can perform CRUD operations on YANG data tree which is an in-memory database designed to store normalized nodes in a tree-like structure.
168
169 In some cases it is clear which NormalizedNode type belongs to which yang statement (e.g. AnyXmlNode, ChoiceNode, LeafNode). However, there are some normalized nodes which are named differently from their yang counterparts. They are listed below:
170
171 * LeafSetNode - leaf-list
172 * OrderedLeafSetNode - leaf-list that is ordered-by user
173 * LeafSetEntryNode - concrete entry in a leaf-list
174 * MapNode - keyed list
175 * OrderedMapNode - keyed list that is ordered-by user
176 * MapEntryNode - concrete entry in a keyed list
177 * UnkeyedListNode - unkeyed list
178 * UnkeyedListEntryNode - concrete entry in an unkeyed list
179
180 In order to create a concrete NormalizedNode object you can use the utility class Builders or ImmutableNodes. These classes can be found in yang-data-impl module and they provide methods for building each type of normalized node. Here is a simple example of building a normalized node:
181
182 [source, java]
183 ----
184 \\ example 1
185 ContainerNode containerNode = Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(moduleQName, "example-container")).build();
186
187 \\ example 2
188 ContainerNode containerNode2 = Builders.containerBuilder(containerSchemaNode).build();
189 ----
190 Both examples produce the same result. NodeIdentifier is one of the four types of YangInstanceIdentifier (these types are described in the javadoc of YangInstanceIdentifier). The purpose of YangInstanceIdentifier is to uniquely identify a particular node in the data tree. In the first example, you have to add NodeIdentifier before building the resulting node. In the second example it is also added using the provided ContainerSchemaNode object. 
191
192 ImmutableNodes class offers similar builder methods and also adds an overloaded method called fromInstanceId() which allows you to create a NormalizedNode object based on YangInstanceIdentifier and SchemaContext. Below is an example which shows the use of this method.
193
194 [source, java]
195 ----
196 YangInstanceIdentifier.NodeIdentifier contId = new YangInstanceIdentifier.NodeIdentifier(QName.create(moduleQName, "example-container");
197
198 NormalizedNode<?, ?> contNode = ImmutableNodes.fromInstanceId(schemaContext, YangInstanceIdentifier.create(contId));
199 ----
200
201 Let us show a more complex example of creating a NormalizedNode. First, consider the following YANG module:
202
203 [source, yang]
204 ----
205 module example-module {
206     namespace "opendaylight.org/example-module";
207     prefix "example";
208
209     container parent-container {
210         container child-container {
211             list parent-ordered-list {
212                 ordered-by user;
213
214                 key "parent-key-leaf";
215
216                 leaf parent-key-leaf {
217                     type string;
218                 }
219
220                 leaf parent-ordinary-leaf {
221                     type string;
222                 }
223
224                 list child-ordered-list {
225                     ordered-by user;
226
227                     key "child-key-leaf";
228
229                     leaf child-key-leaf {
230                         type string;
231                     }
232
233                     leaf child-ordinary-leaf {
234                         type string;
235                     }
236                 }
237             }
238         }
239     }
240 }
241 ----
242
243 In the following example, two normalized nodes based on the module above are written to and read from the data tree.
244
245 [source, java]
246 ----
247 TipProducingDataTree inMemoryDataTree =     InMemoryDataTreeFactory.getInstance().create(TreeType.OPERATIONAL);
248 inMemoryDataTree.setSchemaContext(schemaContext);
249
250 // first data tree modification
251 MapEntryNode parentOrderedListEntryNode = Builders.mapEntryBuilder().withNodeIdentifier(
252 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
253 parentOrderedListQName, parentKeyLeafQName, "pkval1"))
254 .withChild(Builders.leafBuilder().withNodeIdentifier(
255 new YangInstanceIdentifier.NodeIdentifier(parentOrdinaryLeafQName))
256 .withValue("plfval1").build()).build();
257
258 OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
259 new YangInstanceIdentifier.NodeIdentifier(parentOrderedListQName))
260 .withChild(parentOrderedListEntryNode).build();
261
262 ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
263 new YangInstanceIdentifier.NodeIdentifier(parentContainerQName))
264 .withChild(Builders.containerBuilder().withNodeIdentifier(
265 new NodeIdentifier(childContainerQName)).withChild(parentOrderedListNode).build()).build();
266
267 YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainerQName);
268
269 DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
270 treeModification.write(path1, parentContainerNode);
271
272 // second data tree modification
273 MapEntryNode childOrderedListEntryNode = Builders.mapEntryBuilder().withNodeIdentifier(
274 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
275 childOrderedListQName, childKeyLeafQName, "chkval1"))
276 .withChild(Builders.leafBuilder().withNodeIdentifier(
277 new YangInstanceIdentifier.NodeIdentifier(childOrdinaryLeafQName))
278 .withValue("chlfval1").build()).build();
279
280 OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
281 new YangInstanceIdentifier.NodeIdentifier(childOrderedListQName))
282 .withChild(childOrderedListEntryNode).build();
283
284 ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
285 ImmutableMap<QName, Object> keys = builder.put(parentKeyLeafQName, "pkval1").build();
286
287 YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainerQName).node(childContainerQName)
288 .node(parentOrderedListQName).node(new NodeIdentifierWithPredicates(parentOrderedListQName, keys)).node(childOrderedListQName);
289
290 treeModification.write(path2, childOrderedListNode);
291 treeModification.ready();
292 inMemoryDataTree.validate(treeModification);
293 inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
294
295 DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
296 Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
297 Optional<NormalizedNode<?, ?>> readNode2 = snapshotAfterCommits.readNode(path2);
298 ----
299 First comes the creation of in-memory data tree instance. The schema context (containing the model mentioned above) of this tree is set. After that, two normalized nodes are built. The first one consists of a parent container, a child container and a parent ordered list which contains a key leaf and an ordinary leaf. The second normalized node is a child ordered list that also contains a key leaf and an ordinary leaf.
300
301 In order to add a child node to a node, method withChild() is used. It takes a NormalizedNode as argument. When creating a list entry, YangInstanceIdentifier.NodeIdentifierWithPredicates should be used as its identifier. Its arguments are the QName of the list, QName of the list key and the value of the key. Method withValue() specifies a value for the ordinary leaf in the list.
302
303 Before writing a node to the data tree, a path (YangInstanceIdentifier) which determines its place in the data tree needs to be defined. The path of the first normalized node starts at the parent container. The path of the second normalized node points to the child ordered list contained in the parent ordered list entry specified by the key value "pkval1".
304
305 Write operation is performed with both normalized nodes mentioned earlier. It consist of several steps. The first step is to instantiate a DataTreeModification object based on a DataTreeSnapshot. DataTreeSnapshot gives you the current state of the data tree. Then comes the write operation which writes a normalized node at the provided path in the data tree. After doing both write operations, method ready() has to be called, marking the modification as ready for application to the data tree. No further operations within the modification are allowed. The modification is then validated - checked whether it can be applied to the data tree. Finally we commit it to the data tree.
306
307 Now you can access the written nodes. In order to do this, you have to create a new DataTreeSnapshot instance and call the method readNode() with path argument pointing to a particular node in the tree.
308
309 === Serialization / deserialization of YANG Data
310
311 === Introducing schema source repositories
312
313 === Writing YANG driven generators
314
315 === Introducing specific extension support for YANG parser
316
317 === Diagnostics