Merge "Changed NetconfDeviceDatastoreAdapter and NetconfDeviceTopologyAdapter to...
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / TestUtils.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.restconf.impl.test;
9
10 import static org.junit.Assert.assertNotNull;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.when;
15 import com.google.common.base.Preconditions;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import java.io.BufferedReader;
18 import java.io.ByteArrayOutputStream;
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.FileReader;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStreamWriter;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.sql.Date;
28 import java.text.ParseException;
29 import java.text.SimpleDateFormat;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 import javax.ws.rs.WebApplicationException;
38 import javax.ws.rs.ext.MessageBodyReader;
39 import javax.ws.rs.ext.MessageBodyWriter;
40 import javax.xml.parsers.DocumentBuilder;
41 import javax.xml.parsers.DocumentBuilderFactory;
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.transform.OutputKeys;
44 import javax.xml.transform.Transformer;
45 import javax.xml.transform.TransformerException;
46 import javax.xml.transform.TransformerFactory;
47 import javax.xml.transform.dom.DOMSource;
48 import javax.xml.transform.stream.StreamResult;
49 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
50 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
51 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
52 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
53 import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
54 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
55 import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
56 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfError;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
59 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
60 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
61 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
62 import org.opendaylight.yangtools.yang.common.QName;
63 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
64 import org.opendaylight.yangtools.yang.data.api.Node;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
69 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
70 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
71 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
72 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
73 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
74 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
75 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
76 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
77 import org.opendaylight.yangtools.yang.model.api.Module;
78 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
79 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
80 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83 import org.w3c.dom.Document;
84 import org.xml.sax.SAXException;
85
86 public final class TestUtils {
87
88     private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class);
89
90     private final static YangContextParser parser = new YangParserImpl();
91
92     private static Set<Module> loadModules(final String resourceDirectory) throws FileNotFoundException {
93         final File testDir = new File(resourceDirectory);
94         final String[] fileList = testDir.list();
95         final List<File> testFiles = new ArrayList<File>();
96         if (fileList == null) {
97             throw new FileNotFoundException(resourceDirectory);
98         }
99         for (int i = 0; i < fileList.length; i++) {
100             final String fileName = fileList[i];
101             if (new File(testDir, fileName).isDirectory() == false) {
102                 testFiles.add(new File(testDir, fileName));
103             }
104         }
105         return parser.parseYangModels(testFiles);
106     }
107
108     public static Set<Module> loadModulesFrom(final String yangPath) {
109         try {
110             return TestUtils.loadModules(TestUtils.class.getResource(yangPath).getPath());
111         } catch (final FileNotFoundException e) {
112             LOG.error("Yang files at path: " + yangPath + " weren't loaded.");
113         }
114
115         return null;
116     }
117
118     public static SchemaContext loadSchemaContext(final Set<Module> modules) {
119         return parser.resolveSchemaContext(modules);
120     }
121
122     public static SchemaContext loadSchemaContext(final String resourceDirectory) throws FileNotFoundException {
123         return parser.resolveSchemaContext(loadModulesFrom(resourceDirectory));
124     }
125
126     public static Module findModule(final Set<Module> modules, final String moduleName) {
127         for (final Module module : modules) {
128             if (module.getName().equals(moduleName)) {
129                 return module;
130             }
131         }
132         return null;
133     }
134
135     public static Document loadDocumentFrom(final InputStream inputStream) {
136         try {
137             final DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
138             final DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
139             return docBuilder.parse(inputStream);
140         } catch (SAXException | IOException | ParserConfigurationException e) {
141             LOG.error("Error during loading Document from XML", e);
142             return null;
143         }
144     }
145
146     public static String getDocumentInPrintableForm(final Document doc) {
147         Preconditions.checkNotNull(doc);
148         try {
149             final ByteArrayOutputStream out = new ByteArrayOutputStream();
150             final TransformerFactory tf = TransformerFactory.newInstance();
151             final Transformer transformer = tf.newTransformer();
152             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
153             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
154             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
155             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
156             transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
157
158             transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, "UTF-8")));
159             final byte[] charData = out.toByteArray();
160             return new String(charData, "UTF-8");
161         } catch (IOException | TransformerException e) {
162             final String msg = "Error during transformation of Document into String";
163             LOG.error(msg, e);
164             return msg;
165         }
166
167     }
168
169     /**
170      * @deprecated method will be removed in Lithium release
171      *      we don't wish to use Node and CompositeNode anywhere -
172      *
173      * Fill missing data (namespaces) and build correct data type in {@code compositeNode} according to
174      * {@code dataSchemaNode}. The method {@link RestconfImpl#createConfigurationData createConfigurationData} is used
175      * because it contains calling of method {code normalizeNode}
176      */
177     @Deprecated
178     public static void normalizeCompositeNode(final Node<?> node, final Set<Module> modules, final String schemaNodePath) {
179         final RestconfImpl restconf = RestconfImpl.getInstance();
180         ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext(modules));
181         prepareMocksForRestconf(modules, restconf);
182
183         final InstanceIdentifierContext iiContext = ControllerContext.getInstance().toInstanceIdentifier(schemaNodePath);
184         final DOMMountPoint mountPoint = iiContext.getMountPoint();
185         final CompositeNode value = RestconfImpl.getInstance().normalizeNode(node, (DataSchemaNode) iiContext.getSchemaNode(), mountPoint);
186         final NormalizedNode<?, ?> normNodePayload = compositeNodeToDatastoreNormalizedNode(value, (DataSchemaNode) iiContext.getSchemaNode());
187         final NormalizedNodeContext normlNodeContext = new NormalizedNodeContext(iiContext, normNodePayload);
188
189         restconf.updateConfigurationData(schemaNodePath, normlNodeContext);
190     }
191
192     /**
193      * Searches module with name {@code searchedModuleName} in {@code modules}. If module name isn't specified and
194      * module set has only one element then this element is returned.
195      *
196      */
197     public static Module resolveModule(final String searchedModuleName, final Set<Module> modules) {
198         assertNotNull("Modules can't be null.", modules);
199         if (searchedModuleName != null) {
200             for (final Module m : modules) {
201                 if (m.getName().equals(searchedModuleName)) {
202                     return m;
203                 }
204             }
205         } else if (modules.size() == 1) {
206             return modules.iterator().next();
207         }
208         return null;
209     }
210
211     public static DataSchemaNode resolveDataSchemaNode(final String searchedDataSchemaName, final Module module) {
212         assertNotNull("Module can't be null", module);
213
214         if (searchedDataSchemaName != null) {
215             for (final DataSchemaNode dsn : module.getChildNodes()) {
216                 if (dsn.getQName().getLocalName().equals(searchedDataSchemaName)) {
217                     return dsn;
218                 }
219             }
220         } else if (module.getChildNodes().size() == 1) {
221             return module.getChildNodes().iterator().next();
222         }
223         return null;
224     }
225
226     public static QName buildQName(final String name, final String uri, final String date, final String prefix) {
227         try {
228             final URI u = new URI(uri);
229             Date dt = null;
230             if (date != null) {
231                 dt = Date.valueOf(date);
232             }
233             return QName.create(u, dt, name);
234         } catch (final URISyntaxException e) {
235             return null;
236         }
237     }
238
239     public static QName buildQName(final String name, final String uri, final String date) {
240         return buildQName(name, uri, date, null);
241     }
242
243     public static QName buildQName(final String name) {
244         return buildQName(name, "", null);
245     }
246
247     private static void addDummyNamespaceToAllNodes(final NodeWrapper<?> wrappedNode) throws URISyntaxException {
248         wrappedNode.setNamespace(new URI(""));
249         if (wrappedNode instanceof CompositeNodeWrapper) {
250             for (final NodeWrapper<?> childNodeWrapper : ((CompositeNodeWrapper) wrappedNode).getValues()) {
251                 addDummyNamespaceToAllNodes(childNodeWrapper);
252             }
253         }
254     }
255
256     private static void prepareMocksForRestconf(final Set<Module> modules, final RestconfImpl restconf) {
257         final ControllerContext controllerContext = ControllerContext.getInstance();
258         final BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
259
260         controllerContext.setSchemas(TestUtils.loadSchemaContext(modules));
261
262         when(mockedBrokerFacade.commitConfigurationDataPut(any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
263                 .thenReturn(mock(CheckedFuture.class));
264
265         restconf.setControllerContext(controllerContext);
266         restconf.setBroker(mockedBrokerFacade);
267     }
268
269     public static Node<?> readInputToCnSn(final String path, final boolean dummyNamespaces,
270             final MessageBodyReader<Node<?>> reader) throws WebApplicationException {
271
272         final InputStream inputStream = TestUtils.class.getResourceAsStream(path);
273         try {
274             final Node<?> node = reader.readFrom(null, null, null, null, null, inputStream);
275             assertTrue(node instanceof CompositeNodeWrapper);
276             if (dummyNamespaces) {
277                 try {
278                     TestUtils.addDummyNamespaceToAllNodes((CompositeNodeWrapper) node);
279                     return ((CompositeNodeWrapper) node).unwrap();
280                 } catch (final URISyntaxException e) {
281                     LOG.error(e.getMessage());
282                     assertTrue(e.getMessage(), false);
283                 }
284             }
285             return node;
286         } catch (final IOException e) {
287             LOG.error(e.getMessage());
288             assertTrue(e.getMessage(), false);
289         }
290         return null;
291     }
292
293 //    public static Node<?> readInputToCnSnNew(String path, MessageBodyReader<Node<?>> reader) throws WebApplicationException {
294 //        InputStream inputStream = TestUtils.class.getResourceAsStream(path);
295 //        try {
296 //            return reader.readFrom(null, null, null, null, null, inputStream);
297 //        } catch (IOException e) {
298 //            LOG.error(e.getMessage());
299 //            assertTrue(e.getMessage(), false);
300 //        }
301 //        return null;
302 //    }
303
304     public static Node<?> readInputToCnSn(final String path, final MessageBodyReader<Node<?>> reader) {
305         return readInputToCnSn(path, false, reader);
306     }
307
308     public static String writeCompNodeWithSchemaContextToOutput(final Node<?> node, final Set<Module> modules,
309             final DataSchemaNode dataSchemaNode, final MessageBodyWriter<StructuredData> messageBodyWriter) throws IOException,
310             WebApplicationException {
311
312         assertNotNull(dataSchemaNode);
313         assertNotNull("Composite node can't be null", node);
314         final ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
315
316         ControllerContext.getInstance().setSchemas(loadSchemaContext(modules));
317
318         assertTrue(node instanceof CompositeNode);
319         messageBodyWriter.writeTo(new StructuredData((CompositeNode)node, dataSchemaNode, null), null, null, null, null,
320                 null, byteArrayOS);
321
322         return byteArrayOS.toString();
323     }
324
325     public static String loadTextFile(final String filePath) throws IOException {
326         final FileReader fileReader = new FileReader(filePath);
327         final BufferedReader bufReader = new BufferedReader(fileReader);
328
329         String line = null;
330         final StringBuilder result = new StringBuilder();
331         while ((line = bufReader.readLine()) != null) {
332             result.append(line);
333         }
334         bufReader.close();
335         return result.toString();
336     }
337
338     private static Pattern patternForStringsSeparatedByWhiteChars(final String... substrings) {
339         final StringBuilder pattern = new StringBuilder();
340         pattern.append(".*");
341         for (final String substring : substrings) {
342             pattern.append(substring);
343             pattern.append("\\s*");
344         }
345         pattern.append(".*");
346         return Pattern.compile(pattern.toString(), Pattern.DOTALL);
347     }
348
349     public static boolean containsStringData(final String jsonOutput, final String... substrings) {
350         final Pattern pattern = patternForStringsSeparatedByWhiteChars(substrings);
351         final Matcher matcher = pattern.matcher(jsonOutput);
352         return matcher.matches();
353     }
354
355     public static NormalizedNode compositeNodeToDatastoreNormalizedNode(final CompositeNode compositeNode,
356             final DataSchemaNode schema) {
357         final List<Node<?>> lst = new ArrayList<Node<?>>();
358         lst.add(compositeNode);
359         if (schema instanceof ContainerSchemaNode) {
360             return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
361                     .parse(lst, (ContainerSchemaNode) schema);
362         } else if (schema instanceof ListSchemaNode) {
363             return CnSnToNormalizedNodeParserFactory.getInstance().getMapNodeParser()
364                     .parse(lst, (ListSchemaNode) schema);
365         }
366
367         LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
368
369         throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
370                 "It wasn't possible to translate specified data to datastore readable form."));
371     }
372
373     public static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String localName, final String namespace,
374             final String revision) throws ParseException {
375         return new YangInstanceIdentifier.NodeIdentifier(QName.create(namespace, revision, localName));
376     }
377
378     public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName,
379             final String namespace, final String revision, final Map<String, Object> keys) throws ParseException {
380         final Map<QName, Object> predicate = new HashMap<>();
381         for (final String key : keys.keySet()) {
382             predicate.put(QName.create(namespace, revision, key), keys.get(key));
383         }
384
385         return new YangInstanceIdentifier.NodeIdentifierWithPredicates(
386
387         QName.create(namespace, revision, localName), predicate);
388     }
389
390     public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName,
391             final String namespace, final String revision, final String... keysAndValues) throws ParseException {
392         final java.util.Date date = new SimpleDateFormat("yyyy-MM-dd").parse(revision);
393         if (keysAndValues.length % 2 != 0) {
394             new IllegalArgumentException("number of keys argument have to be divisible by 2 (map)");
395         }
396         final Map<QName, Object> predicate = new HashMap<>();
397
398         int i = 0;
399         while (i < keysAndValues.length) {
400             predicate.put(QName.create(namespace, revision, keysAndValues[i++]), keysAndValues[i++]);
401         }
402
403         return new YangInstanceIdentifier.NodeIdentifierWithPredicates(QName.create(namespace, revision, localName),
404                 predicate);
405     }
406
407     /**
408      * @deprecated method has to be removed for Lithium release
409      *          so please use prepareNormalizedNodeWithIetfInterfacesInterfacesData method
410      */
411     @Deprecated
412     public static CompositeNode prepareCompositeNodeWithIetfInterfacesInterfacesData() {
413         final CompositeNodeBuilder<ImmutableCompositeNode> interfaceBuilder = ImmutableCompositeNode.builder();
414         interfaceBuilder.addLeaf(buildQName("name", "dummy", "2014-07-29"), "eth0");
415         interfaceBuilder.addLeaf(buildQName("type", "dummy", "2014-07-29"), "ethernetCsmacd");
416         interfaceBuilder.addLeaf(buildQName("enabled", "dummy", "2014-07-29"), "false");
417         interfaceBuilder.addLeaf(buildQName("description", "dummy", "2014-07-29"), "some interface");
418         return interfaceBuilder.toInstance();
419     }
420
421     static NormalizedNode<?,?> prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException {
422         final String ietfInterfacesDate = "2013-07-04";
423         final String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
424         final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryNode = ImmutableMapEntryNodeBuilder.create();
425
426         final Map<String, Object> predicates = new HashMap<>();
427         predicates.put("name", "eth0");
428
429         mapEntryNode.withNodeIdentifier(getNodeIdentifierPredicate("interface", namespace, ietfInterfacesDate,
430                 predicates));
431         mapEntryNode
432                 .withChild(new ImmutableLeafNodeBuilder<String>()
433                         .withNodeIdentifier(getNodeIdentifier("name", namespace, ietfInterfacesDate)).withValue("eth0")
434                         .build());
435         mapEntryNode.withChild(new ImmutableLeafNodeBuilder<String>()
436                 .withNodeIdentifier(getNodeIdentifier("type", namespace, ietfInterfacesDate))
437                 .withValue("ethernetCsmacd").build());
438         mapEntryNode.withChild(new ImmutableLeafNodeBuilder<Boolean>()
439                 .withNodeIdentifier(getNodeIdentifier("enabled", namespace, ietfInterfacesDate))
440                 .withValue(Boolean.FALSE).build());
441         mapEntryNode.withChild(new ImmutableLeafNodeBuilder<String>()
442                 .withNodeIdentifier(getNodeIdentifier("description", namespace, ietfInterfacesDate))
443                 .withValue("some interface").build());
444
445         return mapEntryNode.build();
446     }
447 }