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