Bump upstreams to SNAPSHOTs
[netconf.git] / netconf / sal-netconf-connector / src / test / java / org / opendaylight / netconf / sal / connect / netconf / listener / NetconfDeviceCommunicatorTest.java
1 /*
2  * Copyright (c) 2014 Brocade Communications 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.netconf.sal.connect.netconf.listener;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.eq;
16 import static org.mockito.ArgumentMatchers.same;
17 import static org.mockito.Mockito.doNothing;
18 import static org.mockito.Mockito.doReturn;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.never;
21 import static org.mockito.Mockito.reset;
22 import static org.mockito.Mockito.spy;
23 import static org.mockito.Mockito.timeout;
24 import static org.mockito.Mockito.verify;
25 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
26
27 import com.google.common.base.CharMatcher;
28 import com.google.common.base.Strings;
29 import com.google.common.collect.Sets;
30 import com.google.common.util.concurrent.ListenableFuture;
31 import io.netty.channel.ChannelFuture;
32 import io.netty.channel.EventLoopGroup;
33 import io.netty.channel.nio.NioEventLoopGroup;
34 import io.netty.util.HashedWheelTimer;
35 import io.netty.util.Timer;
36 import io.netty.util.concurrent.Future;
37 import io.netty.util.concurrent.GenericFutureListener;
38 import io.netty.util.concurrent.GlobalEventExecutor;
39 import java.io.ByteArrayInputStream;
40 import java.net.InetSocketAddress;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.UUID;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 import javax.xml.parsers.ParserConfigurationException;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.mockito.ArgumentCaptor;
52 import org.mockito.Mock;
53 import org.mockito.junit.MockitoJUnitRunner;
54 import org.opendaylight.netconf.api.NetconfMessage;
55 import org.opendaylight.netconf.api.NetconfTerminationReason;
56 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
57 import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
58 import org.opendaylight.netconf.client.NetconfClientSession;
59 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
60 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
61 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
62 import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
63 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategy;
64 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
65 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
66 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
67 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
68 import org.opendaylight.yangtools.util.xml.UntrustedXML;
69 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
70 import org.opendaylight.yangtools.yang.common.ErrorTag;
71 import org.opendaylight.yangtools.yang.common.ErrorType;
72 import org.opendaylight.yangtools.yang.common.QName;
73 import org.opendaylight.yangtools.yang.common.RpcError;
74 import org.opendaylight.yangtools.yang.common.RpcResult;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77 import org.w3c.dom.Document;
78 import org.w3c.dom.Element;
79
80 @RunWith(MockitoJUnitRunner.StrictStubs.class)
81 public class NetconfDeviceCommunicatorTest {
82
83     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceCommunicatorTest.class);
84
85     @Mock
86     NetconfClientSession mockSession;
87
88     @Mock
89     RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> mockDevice;
90
91     NetconfDeviceCommunicator communicator;
92
93     @Before
94     public void setUp() throws Exception {
95         communicator = new NetconfDeviceCommunicator(
96                 new RemoteDeviceId("test", InetSocketAddress.createUnresolved("localhost", 22)), mockDevice, 10);
97     }
98
99     void setupSession() {
100         doReturn(Collections.<String>emptySet()).when(mockSession).getServerCapabilities();
101         doNothing().when(mockDevice).onRemoteSessionUp(any(NetconfSessionPreferences.class),
102                 any(NetconfDeviceCommunicator.class));
103         communicator.onSessionUp(mockSession);
104     }
105
106     private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
107         return sendRequest(UUID.randomUUID().toString(), true);
108     }
109
110     @SuppressWarnings("unchecked")
111     private ListenableFuture<RpcResult<NetconfMessage>> sendRequest(final String messageID,
112                                                                     final boolean doLastTest) throws Exception {
113         Document doc = UntrustedXML.newDocumentBuilder().newDocument();
114         Element element = doc.createElement("request");
115         element.setAttribute("message-id", messageID);
116         doc.appendChild(element);
117         NetconfMessage message = new NetconfMessage(doc);
118
119         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
120         doReturn(mockChannelFuture).when(mockChannelFuture)
121                 .addListener(any(GenericFutureListener.class));
122         doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
123
124         ListenableFuture<RpcResult<NetconfMessage>> resultFuture =
125                 communicator.sendRequest(message, QName.create("", "mockRpc"));
126         if (doLastTest) {
127             assertNotNull("ListenableFuture is null", resultFuture);
128         }
129         return resultFuture;
130     }
131
132     @Test
133     public void testOnSessionUp() {
134         String testCapability = "urn:opendaylight:params:xml:ns:test?module=test-module&revision=2014-06-02";
135         Collection<String> serverCapabilities =
136                 Sets.newHashSet(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(),
137                         NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(),
138                         testCapability);
139         doReturn(serverCapabilities).when(mockSession).getServerCapabilities();
140
141         ArgumentCaptor<NetconfSessionPreferences> netconfSessionPreferences =
142                 ArgumentCaptor.forClass(NetconfSessionPreferences.class);
143         doNothing().when(mockDevice).onRemoteSessionUp(netconfSessionPreferences.capture(), eq(communicator));
144
145         communicator.onSessionUp(mockSession);
146
147         verify(mockSession).getServerCapabilities();
148         verify(mockDevice).onRemoteSessionUp(netconfSessionPreferences.capture(), eq(communicator));
149
150         NetconfSessionPreferences actualCapabilites = netconfSessionPreferences.getValue();
151         assertEquals("containsModuleCapability", true, actualCapabilites.containsNonModuleCapability(
152                 NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()));
153         assertEquals("containsModuleCapability", false, actualCapabilites.containsNonModuleCapability(testCapability));
154         assertEquals("getModuleBasedCaps", Sets.newHashSet(
155                 QName.create("urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module")),
156                 actualCapabilites.getModuleBasedCaps());
157         assertEquals("isRollbackSupported", true, actualCapabilites.isRollbackSupported());
158         assertEquals("isMonitoringSupported", true, actualCapabilites.isMonitoringSupported());
159     }
160
161     @SuppressWarnings("unchecked")
162     @Test(timeout = 5000)
163     public void testOnSessionDown() throws Exception {
164         setupSession();
165
166         ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest();
167         final ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest();
168
169         doNothing().when(mockDevice).onRemoteSessionDown();
170
171         communicator.onSessionDown(mockSession, new Exception("mock ex"));
172
173         verifyErrorRpcResult(resultFuture1.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
174         verifyErrorRpcResult(resultFuture2.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
175
176         verify(mockDevice).onRemoteSessionDown();
177
178         reset(mockDevice);
179
180         communicator.onSessionDown(mockSession, new Exception("mock ex"));
181
182         verify(mockDevice, never()).onRemoteSessionDown();
183     }
184
185     @Test
186     public void testOnSessionTerminated() throws Exception {
187         setupSession();
188
189         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest();
190
191         doNothing().when(mockDevice).onRemoteSessionDown();
192
193         String reasonText = "testing terminate";
194         NetconfTerminationReason reason = new NetconfTerminationReason(reasonText);
195         communicator.onSessionTerminated(mockSession, reason);
196
197         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
198         assertEquals("RpcError message", reasonText, rpcError.getMessage());
199
200         verify(mockDevice).onRemoteSessionDown();
201     }
202
203     @Test
204     public void testClose() throws Exception {
205         communicator.close();
206         verify(mockDevice, never()).onRemoteSessionDown();
207     }
208
209     @SuppressWarnings({"rawtypes", "unchecked"})
210     @Test
211     public void testSendRequest() throws Exception {
212         setupSession();
213
214         NetconfMessage message = new NetconfMessage(UntrustedXML.newDocumentBuilder().newDocument());
215         QName rpc = QName.create("", "mockRpc");
216
217         ArgumentCaptor<GenericFutureListener> futureListener =
218                 ArgumentCaptor.forClass(GenericFutureListener.class);
219
220         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
221         doReturn(mockChannelFuture).when(mockChannelFuture).addListener(futureListener.capture());
222         doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
223
224         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest(message, rpc);
225
226         verify(mockSession).sendMessage(same(message));
227
228         assertNotNull("ListenableFuture is null", resultFuture);
229
230         verify(mockChannelFuture).addListener(futureListener.capture());
231         Future<Void> operationFuture = mock(Future.class);
232         doReturn(true).when(operationFuture).isSuccess();
233         futureListener.getValue().operationComplete(operationFuture);
234
235         try {
236             resultFuture.get(1, TimeUnit.MILLISECONDS); // verify it's not cancelled or has an error set
237         } catch (TimeoutException e) {
238             LOG.info("Operation failed due timeout.");
239         } // expected
240     }
241
242     @Test
243     public void testSendRequestWithNoSession() throws Exception {
244         NetconfMessage message = new NetconfMessage(UntrustedXML.newDocumentBuilder().newDocument());
245         QName rpc = QName.create("", "mockRpc");
246
247         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest(message, rpc);
248
249         assertNotNull("ListenableFuture is null", resultFuture);
250
251         // Should have an immediate result
252         RpcResult<NetconfMessage> rpcResult = resultFuture.get(3, TimeUnit.MILLISECONDS);
253
254         verifyErrorRpcResult(rpcResult, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
255     }
256
257     private static NetconfMessage createSuccessResponseMessage(final String messageID)
258             throws ParserConfigurationException {
259         Document doc = UntrustedXML.newDocumentBuilder().newDocument();
260         Element rpcReply =
261                 doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlNetconfConstants.RPC_REPLY_KEY);
262         rpcReply.setAttribute("message-id", messageID);
263         Element element = doc.createElementNS("ns", "data");
264         element.setTextContent(messageID);
265         rpcReply.appendChild(element);
266         doc.appendChild(rpcReply);
267
268         return new NetconfMessage(doc);
269     }
270
271     @SuppressWarnings({ "rawtypes", "unchecked" })
272     @Test
273     public void testSendRequestWithWithSendFailure() throws Exception {
274         setupSession();
275
276         NetconfMessage message = new NetconfMessage(UntrustedXML.newDocumentBuilder().newDocument());
277         QName rpc = QName.create("", "mockRpc");
278
279         ArgumentCaptor<GenericFutureListener> futureListener =
280                 ArgumentCaptor.forClass(GenericFutureListener.class);
281
282         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
283         doReturn(mockChannelFuture).when(mockChannelFuture).addListener(futureListener.capture());
284         doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
285
286         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest(message, rpc);
287
288         assertNotNull("ListenableFuture is null", resultFuture);
289
290         verify(mockChannelFuture).addListener(futureListener.capture());
291
292         Future<Void> operationFuture = mock(Future.class);
293         doReturn(false).when(operationFuture).isSuccess();
294         doReturn(new Exception("mock error")).when(operationFuture).cause();
295         futureListener.getValue().operationComplete(operationFuture);
296
297         // Should have an immediate result
298         RpcResult<NetconfMessage> rpcResult = resultFuture.get(3, TimeUnit.MILLISECONDS);
299
300         RpcError rpcError = verifyErrorRpcResult(rpcResult, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
301         assertEquals("RpcError message contains \"mock error\"", true,
302                 rpcError.getMessage().contains("mock error"));
303     }
304
305     //Test scenario verifying whether missing message is handled
306     @Test
307     public void testOnMissingResponseMessage() throws Exception {
308
309         setupSession();
310
311         String messageID1 = UUID.randomUUID().toString();
312         ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest(messageID1, true);
313
314         String messageID2 = UUID.randomUUID().toString();
315         ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest(messageID2, true);
316
317         String messageID3 = UUID.randomUUID().toString();
318         ListenableFuture<RpcResult<NetconfMessage>> resultFuture3 = sendRequest(messageID3, true);
319
320         //response messages 1,2 are omitted
321         communicator.onMessage(mockSession, createSuccessResponseMessage(messageID3));
322
323         verifyResponseMessage(resultFuture3.get(), messageID3);
324     }
325
326     @Test
327     public void testOnSuccessfulResponseMessage() throws Exception {
328         setupSession();
329
330         String messageID1 = UUID.randomUUID().toString();
331         ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest(messageID1, true);
332
333         String messageID2 = UUID.randomUUID().toString();
334         final ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest(messageID2, true);
335
336         communicator.onMessage(mockSession, createSuccessResponseMessage(messageID1));
337         communicator.onMessage(mockSession, createSuccessResponseMessage(messageID2));
338
339         verifyResponseMessage(resultFuture1.get(), messageID1);
340         verifyResponseMessage(resultFuture2.get(), messageID2);
341     }
342
343     @Test
344     public void testOnResponseMessageWithError() throws Exception {
345         setupSession();
346
347         String messageID = UUID.randomUUID().toString();
348         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
349
350         communicator.onMessage(mockSession, createErrorResponseMessage(messageID));
351
352         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.RPC, ErrorTag.MISSING_ATTRIBUTE);
353         assertEquals("RpcError message", "Missing attribute", rpcError.getMessage());
354
355         String errorInfo = rpcError.getInfo();
356         assertNotNull("RpcError info is null", errorInfo);
357         assertTrue("Error info contains \"foo\"", errorInfo.contains("<bad-attribute>foo</bad-attribute>"));
358         assertTrue("Error info contains \"bar\"", errorInfo.contains("<bad-element>bar</bad-element>"));
359     }
360
361     @Test
362     public void testOnResponseMessageWithMultipleErrors() throws Exception {
363         setupSession();
364
365         String messageID = UUID.randomUUID().toString();
366         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
367
368         communicator.onMessage(mockSession, createMultiErrorResponseMessage(messageID));
369
370         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED);
371
372         String errorInfo = rpcError.getInfo();
373         assertNotNull("RpcError info is null", errorInfo);
374
375         String errorInfoMessages = rpcError.getInfo();
376         String errMsg1 = "Number of member links configured, i.e [1], "
377                 + "for interface [ae0]is lesser than the required minimum [2].";
378         String errMsg2 = "configuration check-out failed";
379         assertTrue(String.format("Error info contains \"%s\" or \"%s\'", errMsg1, errMsg2),
380                 errorInfoMessages.contains(errMsg1) && errorInfoMessages.contains(errMsg2));
381     }
382
383     /**
384      * Test whether reconnect is scheduled properly.
385      */
386     @Test
387     public void testNetconfDeviceReconnectInCommunicator() {
388         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device =
389                 mock(RemoteDevice.class);
390
391         final TimedReconnectStrategy timedReconnectStrategy =
392                 new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, 10000, 0, 1.0, null, 100L, null);
393         final ReconnectStrategy reconnectStrategy = spy(new ReconnectStrategy() {
394             @Override
395             @Deprecated
396             public int getConnectTimeout() throws Exception {
397                 return timedReconnectStrategy.getConnectTimeout();
398             }
399
400             @Override
401             @Deprecated
402             public Future<Void> scheduleReconnect(final Throwable cause) {
403                 return timedReconnectStrategy.scheduleReconnect(cause);
404             }
405
406             @Override
407             @Deprecated
408             public void reconnectSuccessful() {
409                 timedReconnectStrategy.reconnectSuccessful();
410             }
411         });
412
413         final EventLoopGroup group = new NioEventLoopGroup();
414         final Timer time = new HashedWheelTimer();
415         try {
416             final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(
417                     new RemoteDeviceId("test", InetSocketAddress.createUnresolved("localhost", 22)), device, 10);
418             final NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
419                     .withAddress(new InetSocketAddress("localhost", 65000))
420                     .withReconnectStrategy(reconnectStrategy)
421                     .withConnectStrategyFactory(() -> reconnectStrategy)
422                     .withAuthHandler(new LoginPasswordHandler("admin", "admin"))
423                     .withConnectionTimeoutMillis(10000)
424                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
425                     .withSessionListener(listener)
426                     .build();
427
428             listener.initializeRemoteConnection(new NetconfClientDispatcherImpl(group, group, time), cfg);
429
430             verify(reconnectStrategy,
431                     timeout(TimeUnit.MINUTES.toMillis(4)).times(101)).scheduleReconnect(any(Throwable.class));
432         } finally {
433             time.stop();
434             group.shutdownGracefully();
435         }
436     }
437
438     @Test
439     public void testOnResponseMessageWithWrongMessageID() throws Exception {
440         setupSession();
441
442         String messageID = UUID.randomUUID().toString();
443         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
444
445         communicator.onMessage(mockSession, createSuccessResponseMessage(UUID.randomUUID().toString()));
446
447         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE);
448         assertFalse("RpcError message non-empty", Strings.isNullOrEmpty(rpcError.getMessage()));
449
450         String errorInfo = rpcError.getInfo();
451         assertNotNull("RpcError info is null", errorInfo);
452         assertTrue("Error info contains \"actual-message-id\"", errorInfo.contains("actual-message-id"));
453         assertTrue("Error info contains \"expected-message-id\"", errorInfo.contains("expected-message-id"));
454     }
455
456     @Test
457     public void testConcurrentMessageLimit() throws Exception {
458         setupSession();
459         ArrayList<String> messageID = new ArrayList<>();
460
461         for (int i = 0; i < 10; i++) {
462             messageID.add(UUID.randomUUID().toString());
463             ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID.get(i), false);
464             assertEquals("ListenableFuture is null", true, resultFuture instanceof UncancellableFuture);
465         }
466
467         final String notWorkingMessageID = UUID.randomUUID().toString();
468         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(notWorkingMessageID, false);
469         assertEquals("ListenableFuture is null", false, resultFuture instanceof UncancellableFuture);
470
471         communicator.onMessage(mockSession, createSuccessResponseMessage(messageID.get(0)));
472
473         resultFuture = sendRequest(messageID.get(0), false);
474         assertNotNull("ListenableFuture is null", resultFuture);
475     }
476
477     private static NetconfMessage createMultiErrorResponseMessage(final String messageID) throws Exception {
478         // multiple rpc-errors which simulate actual response like in NETCONF-666
479         String xmlStr = "<nc:rpc-reply xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns:junos=\"http://xml.juniper.net/junos/18.4R1/junos\""
480                 + "           message-id=\"" + messageID + "\">"
481                 + "<nc:rpc-error>\n"
482                 + "<nc:error-type>protocol</nc:error-type>\n"
483                 + "<nc:error-tag>operation-failed</nc:error-tag>\n"
484                 + "<nc:error-severity>error</nc:error-severity>\n"
485                 + "<source-daemon>\n"
486                 + "dcd\n"
487                 + "</source-daemon>\n"
488                 + "<nc:error-message>\n"
489                 + "Number of member links configured, i.e [1], "
490                 + "for interface [ae0]is lesser than the required minimum [2].\n"
491                 + "</nc:error-message>\n"
492                 + "</nc:rpc-error>\n"
493                 + "<nc:rpc-error>\n"
494                 + "<nc:error-type>protocol</nc:error-type>\n"
495                 + "<nc:error-tag>operation-failed</nc:error-tag>\n"
496                 + "<nc:error-severity>error</nc:error-severity>\n"
497                 + "<nc:error-message>\n"
498                 + "configuration check-out failed\n"
499                 + "</nc:error-message>\n"
500                 + "</nc:rpc-error>\n"
501                 + "</nc:rpc-reply>";
502
503         ByteArrayInputStream bis = new ByteArrayInputStream(xmlStr.getBytes());
504         Document doc = UntrustedXML.newDocumentBuilder().parse(bis);
505         return new NetconfMessage(doc);
506     }
507
508     private static NetconfMessage createErrorResponseMessage(final String messageID) throws Exception {
509         String xmlStr = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\""
510                 + "           message-id=\"" + messageID + "\">"
511                 + "  <rpc-error>"
512                 + "    <error-type>rpc</error-type>"
513                 + "    <error-tag>missing-attribute</error-tag>"
514                 + "    <error-severity>error</error-severity>"
515                 + "    <error-message>Missing attribute</error-message>"
516                 + "    <error-info>"
517                 + "      <bad-attribute>foo</bad-attribute>"
518                 + "      <bad-element>bar</bad-element>"
519                 + "    </error-info>"
520                 + "  </rpc-error>"
521                 + "</rpc-reply>";
522
523         ByteArrayInputStream bis = new ByteArrayInputStream(xmlStr.getBytes());
524         Document doc = UntrustedXML.newDocumentBuilder().parse(bis);
525         return new NetconfMessage(doc);
526     }
527
528     private static void verifyResponseMessage(final RpcResult<NetconfMessage> rpcResult, final String dataText) {
529         assertNotNull("RpcResult is null", rpcResult);
530         assertTrue("isSuccessful", rpcResult.isSuccessful());
531         NetconfMessage messageResult = rpcResult.getResult();
532         assertNotNull("getResult", messageResult);
533 //        List<SimpleNode<?>> nodes = messageResult.getSimpleNodesByName(
534 //                                         QName.create( URI.create( "ns" ), null, "data" ) );
535 //        assertNotNull( "getSimpleNodesByName", nodes );
536 //        assertEquals( "List<SimpleNode<?>> size", 1, nodes.size() );
537 //        assertEquals( "SimpleNode value", dataText, nodes.iterator().next().getValue() );
538     }
539
540     private static RpcError verifyErrorRpcResult(final RpcResult<NetconfMessage> rpcResult,
541                                                  final ErrorType expErrorType, final ErrorTag expErrorTag) {
542         assertNotNull("RpcResult is null", rpcResult);
543         assertFalse("isSuccessful", rpcResult.isSuccessful());
544         assertNotNull("RpcResult errors is null", rpcResult.getErrors());
545         assertEquals("Errors size", 1, rpcResult.getErrors().size());
546         RpcError rpcError = rpcResult.getErrors().iterator().next();
547         assertEquals("getErrorSeverity", ErrorSeverity.ERROR, rpcError.getSeverity());
548         assertEquals("getErrorType", expErrorType, rpcError.getErrorType());
549         assertEquals("getErrorTag", expErrorTag, rpcError.getTag());
550
551         final String msg = rpcError.getMessage();
552         assertNotNull("getMessage is null", msg);
553         assertFalse("getMessage is empty", msg.isEmpty());
554         assertFalse("getMessage is blank", CharMatcher.whitespace().matchesAllOf(msg));
555         return rpcError;
556     }
557 }