f3c9e79f7784f50c41486ee89634c092ee67782d
[hc2vpp.git] /
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.translate.v3po.interfaces.ip;
18
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 import static org.mockito.Matchers.any;
24 import static org.mockito.Matchers.argThat;
25 import static org.mockito.Mockito.doNothing;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.doThrow;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31
32 import com.google.common.base.Optional;
33 import io.fd.honeycomb.translate.v3po.interfaces.ip.subnet.validation.SubnetValidationException;
34 import io.fd.honeycomb.translate.v3po.interfaces.ip.subnet.validation.SubnetValidator;
35 import io.fd.honeycomb.translate.v3po.test.ContextTestUtils;
36 import io.fd.honeycomb.translate.v3po.test.TestHelperUtils;
37 import io.fd.honeycomb.vpp.test.write.WriterCustomizerTest;
38 import io.fd.honeycomb.translate.v3po.util.NamingContext;
39 import io.fd.honeycomb.translate.write.WriteFailedException;
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.concurrent.CompletableFuture;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.mockito.ArgumentCaptor;
47 import org.mockito.ArgumentMatcher;
48 import org.mockito.Captor;
49 import org.mockito.Mock;
50 import org.mockito.Mockito;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4Builder;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.Netmask;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.NetmaskBuilder;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.openvpp.jvpp.VppBaseCallException;
68 import org.openvpp.jvpp.VppInvocationException;
69 import org.openvpp.jvpp.core.dto.IpAddressDetailsReplyDump;
70 import org.openvpp.jvpp.core.dto.SwInterfaceAddDelAddress;
71 import org.openvpp.jvpp.core.dto.SwInterfaceAddDelAddressReply;
72
73 public class Ipv4AddressCustomizerTest extends WriterCustomizerTest {
74
75     private static final String IFC_CTX_NAME = "ifc-test-instance";
76     private static final String IFACE_NAME = "eth0";
77     private static final int IFACE_ID = 123;
78
79     @Captor
80     private ArgumentCaptor<List<Address>> addressesCaptor;
81
82     @Mock
83     private SubnetValidator subnetValidator;
84
85     private NamingContext interfaceContext;
86     private Ipv4AddressCustomizer customizer;
87
88     @Before
89     public void setUp() throws Exception {
90         interfaceContext = new NamingContext("generatedIfaceName", IFC_CTX_NAME);
91
92         customizer = new Ipv4AddressCustomizer(api, interfaceContext, subnetValidator);
93
94         CompletableFuture future = new CompletableFuture();
95         future.complete(new IpAddressDetailsReplyDump());
96
97         when(api.ipAddressDump(Mockito.any())).thenReturn(future);
98         when(writeContext.readAfter(Mockito.any()))
99                 .thenReturn(Optional.of(new Ipv4Builder().setAddress(Collections.emptyList()).build()));
100     }
101
102     private static InstanceIdentifier<Address> getAddressId(final String ifaceName) {
103         return InstanceIdentifier.builder(Interfaces.class)
104                 .child(Interface.class, new InterfaceKey(ifaceName))
105                 .augmentation(Interface1.class)
106                 .child(Ipv4.class)
107                 .child(Address.class)
108                 .build();
109     }
110
111     private void whenSwInterfaceAddDelAddressThenSuccess() {
112         final CompletableFuture<SwInterfaceAddDelAddressReply> replyFuture = new CompletableFuture<>();
113         final SwInterfaceAddDelAddressReply reply = new SwInterfaceAddDelAddressReply();
114         replyFuture.complete(reply);
115         doReturn(replyFuture).when(api).swInterfaceAddDelAddress(any(SwInterfaceAddDelAddress.class));
116     }
117
118     private void whenSwInterfaceAddDelAddressThenFailure() {
119         doReturn(TestHelperUtils.createFutureException()).when(api)
120                 .swInterfaceAddDelAddress(any(SwInterfaceAddDelAddress.class));
121     }
122
123     private void verifySwInterfaceAddDelAddressWasInvoked(final SwInterfaceAddDelAddress expected) throws
124             VppInvocationException {
125         ArgumentCaptor<SwInterfaceAddDelAddress> argumentCaptor =
126                 ArgumentCaptor.forClass(SwInterfaceAddDelAddress.class);
127         verify(api).swInterfaceAddDelAddress(argumentCaptor.capture());
128         verifySwInterfaceAddDelAddressWasInvoked(expected, argumentCaptor.getValue());
129     }
130
131     private void verifySwInterfaceAddDelAddressWasInvoked(final SwInterfaceAddDelAddress expected,
132                                                           final SwInterfaceAddDelAddress actual) throws
133             VppInvocationException {
134         assertArrayEquals(expected.address, actual.address);
135         assertEquals(expected.addressLength, actual.addressLength);
136         assertEquals(expected.delAll, actual.delAll);
137         assertEquals(expected.isAdd, actual.isAdd);
138         assertEquals(expected.isIpv6, actual.isIpv6);
139         assertEquals(expected.swIfIndex, actual.swIfIndex);
140     }
141
142     @Test
143     public void testAddPrefixLengthIpv4Address() throws Exception {
144         doNothing().when(subnetValidator).checkNotAddingToSameSubnet(Mockito.anyList());
145
146         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
147         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
148
149         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
150         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
151         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
152
153         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
154         whenSwInterfaceAddDelAddressThenSuccess();
155
156         customizer.writeCurrentAttributes(id, data, writeContext);
157
158         verifySwInterfaceAddDelAddressWasInvoked(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
159                 (byte) 1, (byte) 24));
160     }
161
162     @Test
163     public void testAddPrefixLengthIpv4AddressFailed() throws Exception {
164         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
165         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
166
167         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
168         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
169         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
170
171         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
172         whenSwInterfaceAddDelAddressThenFailure();
173
174         try {
175             customizer.writeCurrentAttributes(id, data, writeContext);
176         } catch (WriteFailedException e) {
177             assertTrue(e.getCause() instanceof VppBaseCallException);
178             verifySwInterfaceAddDelAddressWasInvoked(
179                     generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
180                             (byte) 1, (byte) 24));
181             return;
182         }
183         fail("WriteFailedException was expected");
184     }
185
186     @Test
187     public void testAddPrefixLengthIpv4AddressConflicted() throws Exception {
188
189         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
190         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
191
192         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
193         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
194         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
195         final List<Address> addressList = Arrays.asList(data);
196
197         //throws when validation invoked
198         doThrow(SubnetValidationException.forConflictingData((short) 24, Arrays.asList(data))).when(subnetValidator)
199                 .checkNotAddingToSameSubnet(addressList);
200
201         //fake data return from WriteContext
202         doReturn(Optional.of(new Ipv4Builder().setAddress(addressList).build())).when(writeContext)
203                 .readAfter(argThat(matchInstanceIdentifier(Ipv4.class)));
204
205         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
206
207         try {
208             customizer.writeCurrentAttributes(id, data, writeContext);
209         } catch (WriteFailedException e) {
210             //verifies if cause of exception is correct type
211             assertTrue(e.getCause() instanceof SubnetValidationException);
212
213             //verify that validation call was invoked with data from writeContext
214             verify(subnetValidator, times(1)).checkNotAddingToSameSubnet(addressesCaptor.capture());
215             assertEquals(addressList, addressesCaptor.getValue());
216         }
217
218     }
219
220     private static ArgumentMatcher<InstanceIdentifier<?>> matchInstanceIdentifier(
221             Class<?> desiredClass) {
222         return new ArgumentMatcher<InstanceIdentifier<?>>() {
223             @Override
224             public boolean matches(final Object o) {
225                 return o instanceof InstanceIdentifier && ((InstanceIdentifier) o).getTargetType().equals(desiredClass);
226             }
227         };
228     }
229
230     private SwInterfaceAddDelAddress generateSwInterfaceAddDelAddressRequest(final byte[] address, final byte isAdd,
231                                                                              final byte prefixLength) {
232         final SwInterfaceAddDelAddress request = new SwInterfaceAddDelAddress();
233         request.swIfIndex = IFACE_ID;
234         request.isAdd = isAdd;
235         request.isIpv6 = 0;
236         request.delAll = 0;
237         request.addressLength = prefixLength;
238         request.address = address;
239         return request;
240     }
241
242     @Test
243     public void testDeletePrefixLengthIpv4Address() throws Exception {
244         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
245
246         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
247         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
248         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
249
250         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
251         whenSwInterfaceAddDelAddressThenSuccess();
252
253         customizer.deleteCurrentAttributes(id, data, writeContext);
254
255         verifySwInterfaceAddDelAddressWasInvoked(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
256                 (byte) 0, (byte) 24));
257     }
258
259     @Test
260     public void testDeletePrefixLengthIpv4AddressFailed() throws Exception {
261         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
262
263         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
264         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
265         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
266
267         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
268         whenSwInterfaceAddDelAddressThenFailure();
269
270         try {
271             customizer.deleteCurrentAttributes(id, data, writeContext);
272         } catch (WriteFailedException e) {
273             assertTrue(e.getCause() instanceof VppBaseCallException);
274             verifySwInterfaceAddDelAddressWasInvoked(
275                     generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
276                             (byte) 0, (byte) 24));
277             return;
278         }
279         fail("WriteFailedException was expec16ted");
280     }
281
282     private void testSingleNetmask(final int expectedPrefixLength, final String stringMask) throws Exception {
283         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
284         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
285
286         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
287         Netmask subnet = new NetmaskBuilder().setNetmask(new DottedQuad(stringMask)).build();
288         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(subnet).build();
289
290         ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
291
292         final CompletableFuture<SwInterfaceAddDelAddressReply> replyFuture = new CompletableFuture<>();
293         replyFuture.complete(new SwInterfaceAddDelAddressReply());
294         ArgumentCaptor<SwInterfaceAddDelAddress> argumentCaptor =
295                 ArgumentCaptor.forClass(SwInterfaceAddDelAddress.class);
296         doReturn(replyFuture).when(api).swInterfaceAddDelAddress(argumentCaptor.capture());
297
298         customizer.writeCurrentAttributes(id, data, writeContext);
299
300         verifySwInterfaceAddDelAddressWasInvoked(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
301                 (byte) 1, (byte) expectedPrefixLength), argumentCaptor.getValue());
302     }
303
304     private void testSingleIllegalNetmask(final String stringMask) throws Exception {
305         try {
306             final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
307             when(writeContext.readBefore(id)).thenReturn(Optional.absent());
308
309             Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
310             Netmask subnet = new NetmaskBuilder().setNetmask(new DottedQuad(stringMask)).build();
311             Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(subnet).build();
312
313             ContextTestUtils.mockMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
314
315             final CompletableFuture<SwInterfaceAddDelAddressReply> replyFuture = new CompletableFuture<>();
316             replyFuture.complete(new SwInterfaceAddDelAddressReply());
317             ArgumentCaptor<SwInterfaceAddDelAddress> argumentCaptor =
318                     ArgumentCaptor.forClass(SwInterfaceAddDelAddress.class);
319             doReturn(replyFuture).when(api).swInterfaceAddDelAddress(argumentCaptor.capture());
320
321             customizer.writeCurrentAttributes(id, data, writeContext);
322         } catch (IllegalArgumentException e) {
323             return;
324         }
325         fail("IllegalArgumentException expected");
326
327     }
328
329     /**
330      * Test contiguous netmask length from QuadDotted notation
331      */
332     @Test
333     public void testNetmaskLength() throws Exception {
334         testSingleNetmask(1, "128.0.0.0");
335         testSingleNetmask(2, "192.0.0.0");
336         testSingleNetmask(8, "255.0.0.0");
337         testSingleNetmask(9, "255.128.0.0");
338         testSingleNetmask(16, "255.255.0.0");
339         testSingleNetmask(24, "255.255.255.0");
340     }
341
342     @Test
343     public void testNetmaskIllegal() throws Exception {
344         testSingleIllegalNetmask("");
345         testSingleIllegalNetmask(".");
346         testSingleIllegalNetmask(".255");
347         testSingleIllegalNetmask("255");
348         testSingleIllegalNetmask("255.");
349         testSingleIllegalNetmask("255.255");
350         testSingleIllegalNetmask("255.255.0");
351         testSingleIllegalNetmask("255.255.255.");
352         testSingleIllegalNetmask("255.255.255.256");
353         testSingleIllegalNetmask("0.0.0.0");
354         testSingleIllegalNetmask("10.10.10.10");
355         testSingleIllegalNetmask("255.1.255.0");
356         testSingleIllegalNetmask("255.255.255.255");
357     }
358 }