Bug 2372: Removing duplicate call in loadBalancerPoolNorthbound to loadBalancerPoolIn...
[controller.git] / opendaylight / md-sal / sal-netconf-connector / src / test / java / org / opendaylight / controller / 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
9 package org.opendaylight.controller.sal.connect.netconf.listener;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Matchers.any;
15 import static org.mockito.Matchers.eq;
16 import static org.mockito.Matchers.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.verify;
23 import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
24 import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
25
26 import com.google.common.base.Strings;
27 import com.google.common.collect.Sets;
28 import com.google.common.util.concurrent.ListenableFuture;
29 import io.netty.channel.ChannelFuture;
30 import io.netty.util.concurrent.Future;
31 import io.netty.util.concurrent.GenericFutureListener;
32 import java.io.ByteArrayInputStream;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.UUID;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.TimeoutException;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import org.apache.commons.lang3.StringUtils;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.mockito.ArgumentCaptor;
44 import org.mockito.Mock;
45 import org.mockito.MockitoAnnotations;
46 import org.opendaylight.controller.netconf.api.NetconfMessage;
47 import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
48 import org.opendaylight.controller.netconf.client.NetconfClientSession;
49 import org.opendaylight.controller.sal.connect.api.RemoteDevice;
50 import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
51 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
52 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
53 import org.opendaylight.yangtools.yang.common.QName;
54 import org.opendaylight.yangtools.yang.common.RpcError;
55 import org.opendaylight.yangtools.yang.common.RpcResult;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.Element;
58
59 public class NetconfDeviceCommunicatorTest {
60
61     @Mock
62     NetconfClientSession mockSession;
63
64     @Mock
65     RemoteDevice<NetconfSessionCapabilities, NetconfMessage> mockDevice;
66
67     NetconfDeviceCommunicator communicator;
68
69     @Before
70     public void setUp() throws Exception {
71         MockitoAnnotations.initMocks( this );
72
73         communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice );
74     }
75
76     @SuppressWarnings("unchecked")
77     void setupSession()
78     {
79         doReturn( Collections.<String>emptySet() ).when( mockSession ).getServerCapabilities();
80         doNothing().when( mockDevice ).onRemoteSessionUp( any( NetconfSessionCapabilities.class ),
81                                                           any( RemoteDeviceCommunicator.class ) );
82         communicator.onSessionUp( mockSession );
83     }
84
85     private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
86         return sendRequest( UUID.randomUUID().toString() );
87     }
88
89     @SuppressWarnings("unchecked")
90     private ListenableFuture<RpcResult<NetconfMessage>> sendRequest( String messageID ) throws Exception {
91         Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
92         Element element = doc.createElement( "request" );
93         element.setAttribute( "message-id", messageID );
94         doc.appendChild( element );
95         NetconfMessage message = new NetconfMessage( doc );
96
97         ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
98         doReturn( mockChannelFuture ).when( mockChannelFuture )
99             .addListener( any( (GenericFutureListener.class ) ) );
100         doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
101
102         ListenableFuture<RpcResult<NetconfMessage>> resultFuture =
103                                       communicator.sendRequest( message, QName.create( "mock rpc" ) );
104
105         assertNotNull( "ListenableFuture is null", resultFuture );
106         return resultFuture;
107     }
108
109     @Test
110     public void testOnSessionUp() {
111         String testCapability = "urn:opendaylight:params:xml:ns:test?module=test-module&revision=2014-06-02";
112         Collection<String> serverCapabilities =
113                 Sets.newHashSet( NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(),
114                                  NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(),
115                                  testCapability );
116         doReturn( serverCapabilities ).when( mockSession ).getServerCapabilities();
117
118         ArgumentCaptor<NetconfSessionCapabilities> netconfSessionCapabilities =
119                                               ArgumentCaptor.forClass( NetconfSessionCapabilities.class );
120         doNothing().when( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
121
122         communicator.onSessionUp( mockSession );
123
124         verify( mockSession ).getServerCapabilities();
125         verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
126
127         NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue();
128         assertEquals( "containsModuleCapability", true, actualCapabilites.containsNonModuleCapability(
129                 NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()) );
130         assertEquals( "containsModuleCapability", false, actualCapabilites.containsNonModuleCapability(testCapability) );
131         assertEquals( "getModuleBasedCaps", Sets.newHashSet(
132                             QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )),
133                       actualCapabilites.getModuleBasedCaps() );
134         assertEquals( "isRollbackSupported", true, actualCapabilites.isRollbackSupported() );
135         assertEquals( "isMonitoringSupported", true, actualCapabilites.isMonitoringSupported() );
136     }
137
138     @SuppressWarnings("unchecked")
139     @Test(timeout=5000)
140     public void testOnSessionDown() throws Exception {
141         setupSession();
142
143         ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest();
144         ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest();
145
146         doNothing().when( mockDevice ).onRemoteSessionDown();
147
148         communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
149
150         verifyErrorRpcResult( resultFuture1.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
151         verifyErrorRpcResult( resultFuture2.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
152
153         verify( mockDevice ).onRemoteSessionDown();
154
155         reset( mockDevice );
156
157         communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
158
159         verify( mockDevice, never() ).onRemoteSessionDown();
160     }
161
162     @Test
163     public void testOnSessionTerminated() throws Exception {
164         setupSession();
165
166         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest();
167
168         doNothing().when( mockDevice ).onRemoteSessionDown();
169
170         String reasonText = "testing terminate";
171         NetconfTerminationReason reason = new NetconfTerminationReason( reasonText );
172         communicator.onSessionTerminated( mockSession, reason );
173
174         RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.TRANSPORT,
175                                                   "operation-failed" );
176         assertEquals( "RpcError message", reasonText, rpcError.getMessage() );
177
178         verify( mockDevice ).onRemoteSessionDown();
179     }
180
181     @Test
182     public void testClose() throws Exception {
183         communicator.close();
184         verify( mockDevice, never() ).onRemoteSessionDown();
185     }
186
187     @SuppressWarnings({ "rawtypes", "unchecked" })
188     @Test
189     public void testSendRequest() throws Exception {
190         setupSession();
191
192         NetconfMessage message = new NetconfMessage(
193                               DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
194         QName rpc = QName.create( "mock rpc" );
195
196         ArgumentCaptor<GenericFutureListener> futureListener =
197                                             ArgumentCaptor.forClass( GenericFutureListener.class );
198
199         ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
200         doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
201         doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
202
203         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
204
205         verify( mockSession ).sendMessage( same( message ) );
206
207         assertNotNull( "ListenableFuture is null", resultFuture );
208
209         verify( mockChannelFuture ).addListener( futureListener.capture() );
210         Future<Void> operationFuture = mock( Future.class );
211         doReturn( true ).when( operationFuture ).isSuccess();
212         doReturn( true ).when( operationFuture ).isDone();
213         futureListener.getValue().operationComplete( operationFuture );
214
215         try {
216             resultFuture.get( 1, TimeUnit.MILLISECONDS ); // verify it's not cancelled or has an error set
217         }
218         catch( TimeoutException e ) {} // expected
219     }
220
221     @Test
222     public void testSendRequestWithNoSession() throws Exception {
223         NetconfMessage message = new NetconfMessage(
224                               DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
225         QName rpc = QName.create( "mock rpc" );
226
227         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
228
229         assertNotNull( "ListenableFuture is null", resultFuture );
230
231         // Should have an immediate result
232         RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
233
234         verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
235     }
236
237     @SuppressWarnings({ "rawtypes", "unchecked" })
238     @Test
239     public void testSendRequestWithWithSendFailure() throws Exception {
240         setupSession();
241
242         NetconfMessage message = new NetconfMessage(
243                               DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
244         QName rpc = QName.create( "mock rpc" );
245
246         ArgumentCaptor<GenericFutureListener> futureListener =
247                                             ArgumentCaptor.forClass( GenericFutureListener.class );
248
249         ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
250         doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
251         doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
252
253         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
254
255         assertNotNull( "ListenableFuture is null", resultFuture );
256
257         verify( mockChannelFuture ).addListener( futureListener.capture() );
258
259         Future<Void> operationFuture = mock( Future.class );
260         doReturn( false ).when( operationFuture ).isSuccess();
261         doReturn( true ).when( operationFuture ).isDone();
262         doReturn( new Exception( "mock error" ) ).when( operationFuture ).cause();
263         futureListener.getValue().operationComplete( operationFuture );
264
265         // Should have an immediate result
266         RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
267
268         RpcError rpcError = verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
269         assertEquals( "RpcError message contains \"mock error\"", true,
270                     rpcError.getMessage().contains( "mock error" ) );
271     }
272
273     private NetconfMessage createSuccessResponseMessage( String messageID ) throws ParserConfigurationException {
274         Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
275         Element rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY );
276         rpcReply.setAttribute( "message-id", messageID );
277         Element element = doc.createElementNS( "ns", "data" );
278         element.setTextContent( messageID );
279         rpcReply.appendChild( element );
280         doc.appendChild( rpcReply );
281
282         return new NetconfMessage( doc );
283     }
284
285     @Test
286     public void testOnSuccessfulResponseMessage() throws Exception {
287         setupSession();
288
289         String messageID1 = UUID.randomUUID().toString();
290         ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest( messageID1 );
291
292         String messageID2 = UUID.randomUUID().toString();
293         ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest( messageID2 );
294
295         communicator.onMessage( mockSession, createSuccessResponseMessage( messageID1 ) );
296         communicator.onMessage( mockSession, createSuccessResponseMessage( messageID2 ) );
297
298         verifyResponseMessage( resultFuture1.get(), messageID1 );
299         verifyResponseMessage( resultFuture2.get(), messageID2 );
300     }
301
302     @Test
303     public void testOnResponseMessageWithError() throws Exception {
304         setupSession();
305
306         String messageID = UUID.randomUUID().toString();
307         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
308
309         communicator.onMessage( mockSession, createErrorResponseMessage( messageID ) );
310
311         RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.RPC,
312                                                   "missing-attribute" );
313         assertEquals( "RpcError message", "Missing attribute", rpcError.getMessage() );
314
315         String errorInfo = rpcError.getInfo();
316         assertNotNull( "RpcError info is null", errorInfo );
317         assertEquals( "Error info contains \"foo\"", true,
318                       errorInfo.contains( "<bad-attribute>foo</bad-attribute>" ) );
319         assertEquals( "Error info contains \"bar\"", true,
320                       errorInfo.contains( "<bad-element>bar</bad-element>" ) );
321     }
322
323     @Test
324     public void testOnResponseMessageWithWrongMessageID() throws Exception {
325         setupSession();
326
327         String messageID = UUID.randomUUID().toString();
328         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
329
330         communicator.onMessage( mockSession, createSuccessResponseMessage( UUID.randomUUID().toString() ) );
331
332         RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.PROTOCOL,
333                                                   "bad-attribute" );
334         assertEquals( "RpcError message non-empty", true,
335                       !Strings.isNullOrEmpty( rpcError.getMessage() ) );
336
337         String errorInfo = rpcError.getInfo();
338         assertNotNull( "RpcError info is null", errorInfo );
339         assertEquals( "Error info contains \"actual-message-id\"", true,
340                       errorInfo.contains( "actual-message-id" ) );
341         assertEquals( "Error info contains \"expected-message-id\"", true,
342                       errorInfo.contains( "expected-message-id" ) );
343     }
344
345     private NetconfMessage createErrorResponseMessage( String messageID ) throws Exception {
346         String xmlStr =
347             "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"" +
348             "           message-id=\"" + messageID + "\">" +
349             "  <rpc-error>" +
350             "    <error-type>rpc</error-type>" +
351             "    <error-tag>missing-attribute</error-tag>" +
352             "    <error-severity>error</error-severity>" +
353             "    <error-message>Missing attribute</error-message>" +
354             "    <error-info>" +
355             "      <bad-attribute>foo</bad-attribute>" +
356             "      <bad-element>bar</bad-element>" +
357             "    </error-info>" +
358             "  </rpc-error>" +
359             "</rpc-reply>";
360
361         ByteArrayInputStream bis = new ByteArrayInputStream( xmlStr.getBytes() );
362         Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( bis );
363         return new NetconfMessage( doc );
364     }
365
366     private void verifyResponseMessage( RpcResult<NetconfMessage> rpcResult, String dataText ) {
367         assertNotNull( "RpcResult is null", rpcResult );
368         assertEquals( "isSuccessful", true, rpcResult.isSuccessful() );
369         NetconfMessage messageResult = rpcResult.getResult();
370         assertNotNull( "getResult", messageResult );
371 //        List<SimpleNode<?>> nodes = messageResult.getSimpleNodesByName(
372 //                                         QName.create( URI.create( "ns" ), null, "data" ) );
373 //        assertNotNull( "getSimpleNodesByName", nodes );
374 //        assertEquals( "List<SimpleNode<?>> size", 1, nodes.size() );
375 //        assertEquals( "SimpleNode value", dataText, nodes.iterator().next().getValue() );
376     }
377
378     private RpcError verifyErrorRpcResult( RpcResult<NetconfMessage> rpcResult,
379                                            RpcError.ErrorType expErrorType, String expErrorTag ) {
380         assertNotNull( "RpcResult is null", rpcResult );
381         assertEquals( "isSuccessful", false, rpcResult.isSuccessful() );
382         assertNotNull( "RpcResult errors is null", rpcResult.getErrors() );
383         assertEquals( "Errors size", 1, rpcResult.getErrors().size() );
384         RpcError rpcError = rpcResult.getErrors().iterator().next();
385         assertEquals( "getErrorSeverity", RpcError.ErrorSeverity.ERROR, rpcError.getSeverity() );
386         assertEquals( "getErrorType", expErrorType, rpcError.getErrorType() );
387         assertEquals( "getErrorTag", expErrorTag, rpcError.getTag() );
388         assertTrue( "getMessage is empty", StringUtils.isNotEmpty( rpcError.getMessage() ) );
389         return rpcError;
390     }
391 }