HONEYCOMB-58 - Routing Api
[honeycomb.git] / v3po / v3po2vpp / src / test / java / io / fd / honeycomb / translate / v3po / interfaces / ip / Ipv4AddressCustomizerTest.java
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.assertTrue;
20 import static org.junit.Assert.fail;
21 import static org.mockito.Matchers.any;
22 import static org.mockito.Matchers.argThat;
23 import static org.mockito.Mockito.doNothing;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.doThrow;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.times;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.when;
30
31 import com.google.common.base.Optional;
32 import io.fd.honeycomb.translate.v3po.interfaces.ip.subnet.validation.SubnetValidationException;
33 import io.fd.honeycomb.translate.v3po.interfaces.ip.subnet.validation.SubnetValidator;
34 import io.fd.honeycomb.translate.vpp.util.NamingContext;
35 import io.fd.honeycomb.translate.write.WriteFailedException;
36 import io.fd.honeycomb.vpp.test.write.WriterCustomizerTest;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.List;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.mockito.ArgumentMatcher;
43 import org.mockito.Mock;
44 import org.mockito.Mockito;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4Builder;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.Netmask;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.NetmaskBuilder;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
61 import io.fd.vpp.jvpp.VppBaseCallException;
62 import io.fd.vpp.jvpp.core.dto.IpAddressDetailsReplyDump;
63 import io.fd.vpp.jvpp.core.dto.SwInterfaceAddDelAddress;
64 import io.fd.vpp.jvpp.core.dto.SwInterfaceAddDelAddressReply;
65
66 public class Ipv4AddressCustomizerTest extends WriterCustomizerTest {
67
68     private static final String IFC_CTX_NAME = "ifc-test-instance";
69     private static final String IFACE_NAME = "eth0";
70     private static final int IFACE_ID = 123;
71
72     @Mock
73     private SubnetValidator subnetValidator;
74
75     private NamingContext interfaceContext;
76     private Ipv4AddressCustomizer customizer;
77
78     @Before
79     public void setUp() throws Exception {
80         interfaceContext = new NamingContext("generatedIfaceName", IFC_CTX_NAME);
81
82         customizer = new Ipv4AddressCustomizer(api, interfaceContext, subnetValidator);
83
84         doReturn(future(new IpAddressDetailsReplyDump())).when(api).ipAddressDump(any());
85         when(writeContext.readAfter(Mockito.any()))
86                 .thenReturn(Optional.of(new Ipv4Builder().setAddress(Collections.emptyList()).build()));
87     }
88
89     private static InstanceIdentifier<Address> getAddressId(final String ifaceName) {
90         return InstanceIdentifier.builder(Interfaces.class)
91                 .child(Interface.class, new InterfaceKey(ifaceName))
92                 .augmentation(Interface1.class)
93                 .child(Ipv4.class)
94                 .child(Address.class)
95                 .build();
96     }
97
98     private void whenSwInterfaceAddDelAddressThenSuccess() {
99         doReturn(future(new SwInterfaceAddDelAddressReply())).when(api).swInterfaceAddDelAddress(any(SwInterfaceAddDelAddress.class));
100     }
101
102     private void whenSwInterfaceAddDelAddressThenFailure() {
103         doReturn(failedFuture()).when(api).swInterfaceAddDelAddress(any(SwInterfaceAddDelAddress.class));
104     }
105
106     @Test
107     public void testAddPrefixLengthIpv4Address() throws Exception {
108         doNothing().when(subnetValidator).checkNotAddingToSameSubnet(Mockito.anyList());
109
110         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
111         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
112
113         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
114         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
115         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
116
117         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
118         whenSwInterfaceAddDelAddressThenSuccess();
119
120         customizer.writeCurrentAttributes(id, data, writeContext);
121
122         verify(api).swInterfaceAddDelAddress(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
123                 (byte) 1, (byte) 24));
124     }
125
126     @Test
127     public void testAddPrefixLengthIpv4AddressFailed() throws Exception {
128         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
129         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
130
131         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
132         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
133         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
134
135         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
136         whenSwInterfaceAddDelAddressThenFailure();
137
138         try {
139             customizer.writeCurrentAttributes(id, data, writeContext);
140         } catch (WriteFailedException e) {
141             assertTrue(e.getCause() instanceof VppBaseCallException);
142             verify(api).swInterfaceAddDelAddress(
143                     generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
144                             (byte) 1, (byte) 24));
145             return;
146         }
147         fail("WriteFailedException was expected");
148     }
149
150     @Test
151     public void testAddPrefixLengthIpv4AddressConflicted() throws Exception {
152
153         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
154         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
155
156         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
157         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
158         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
159         final List<Address> addressList = Arrays.asList(data);
160
161         //throws when validation invoked
162         doThrow(SubnetValidationException.forConflictingData((short) 24, Arrays.asList(data))).when(subnetValidator)
163                 .checkNotAddingToSameSubnet(addressList);
164
165         //fake data return from WriteContext
166         doReturn(Optional.of(new Ipv4Builder().setAddress(addressList).build())).when(writeContext)
167                 .readAfter(argThat(matchInstanceIdentifier(Ipv4.class)));
168
169         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
170
171         try {
172             customizer.writeCurrentAttributes(id, data, writeContext);
173         } catch (WriteFailedException e) {
174             //verifies if cause of exception is correct type
175             assertTrue(e.getCause() instanceof SubnetValidationException);
176
177             //verify that validation call was invoked with data from writeContext
178             verify(subnetValidator, times(1)).checkNotAddingToSameSubnet(addressList);
179         }
180
181     }
182
183     private static ArgumentMatcher<InstanceIdentifier<?>> matchInstanceIdentifier(
184             Class<?> desiredClass) {
185         return o -> o instanceof InstanceIdentifier && (o.getTargetType().equals(desiredClass));
186     }
187
188     @Test(expected =  WriteFailedException.UpdateFailedException.class)
189     public void testUpdate() throws Exception {
190         final Address data = mock(Address.class);
191         customizer.updateCurrentAttributes(getAddressId(IFACE_NAME), data, data, writeContext);
192     }
193
194     private SwInterfaceAddDelAddress generateSwInterfaceAddDelAddressRequest(final byte[] address, final byte isAdd,
195                                                                              final byte prefixLength) {
196         final SwInterfaceAddDelAddress request = new SwInterfaceAddDelAddress();
197         request.swIfIndex = IFACE_ID;
198         request.isAdd = isAdd;
199         request.isIpv6 = 0;
200         request.delAll = 0;
201         request.addressLength = prefixLength;
202         request.address = address;
203         return request;
204     }
205
206     @Test
207     public void testDeletePrefixLengthIpv4Address() throws Exception {
208         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
209
210         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
211         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
212         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
213
214         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
215         whenSwInterfaceAddDelAddressThenSuccess();
216
217         customizer.deleteCurrentAttributes(id, data, writeContext);
218
219         verify(api).swInterfaceAddDelAddress(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
220                 (byte) 0, (byte) 24));
221     }
222
223     @Test
224     public void testDeletePrefixLengthIpv4AddressFailed() throws Exception {
225         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
226
227         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
228         PrefixLength length = new PrefixLengthBuilder().setPrefixLength(new Integer(24).shortValue()).build();
229         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(length).build();
230
231         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
232         whenSwInterfaceAddDelAddressThenFailure();
233
234         try {
235             customizer.deleteCurrentAttributes(id, data, writeContext);
236         } catch (WriteFailedException e) {
237             assertTrue(e.getCause() instanceof VppBaseCallException);
238             verify(api).swInterfaceAddDelAddress(
239                     generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
240                             (byte) 0, (byte) 24));
241             return;
242         }
243         fail("WriteFailedException was expec16ted");
244     }
245
246     @Test
247     public void testNetmaskFailed() {
248         final int expectedPrefixLength = 1;
249         final String stringMask = "128.0.0.0";
250         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
251         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
252
253         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
254         Netmask subnet = new NetmaskBuilder().setNetmask(new DottedQuad(stringMask)).build();
255         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(subnet).build();
256
257         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
258         whenSwInterfaceAddDelAddressThenFailure();
259
260         try {
261             customizer.writeCurrentAttributes(id, data, writeContext);
262         } catch (WriteFailedException e) {
263             assertTrue(e.getCause() instanceof VppBaseCallException);
264             verify(api).swInterfaceAddDelAddress(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
265                 (byte) 1, (byte) expectedPrefixLength));
266             return;
267         }
268         fail("WriteFailedException was expec16ted");
269
270     }
271
272     private void testSingleNetmask(final int expectedPrefixLength, final String stringMask) throws Exception {
273         final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
274         when(writeContext.readBefore(id)).thenReturn(Optional.absent());
275
276         Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
277         Netmask subnet = new NetmaskBuilder().setNetmask(new DottedQuad(stringMask)).build();
278         Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(subnet).build();
279
280         defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
281         whenSwInterfaceAddDelAddressThenSuccess();
282
283         customizer.writeCurrentAttributes(id, data, writeContext);
284
285         verify(api).swInterfaceAddDelAddress(generateSwInterfaceAddDelAddressRequest(new byte[]{-64, -88, 2, 1},
286             (byte) 1, (byte) expectedPrefixLength));
287     }
288
289     private void testSingleIllegalNetmask(final String stringMask) throws Exception {
290         try {
291             final InstanceIdentifier<Address> id = getAddressId(IFACE_NAME);
292             when(writeContext.readBefore(id)).thenReturn(Optional.absent());
293
294             Ipv4AddressNoZone noZoneIp = new Ipv4AddressNoZone(new Ipv4Address("192.168.2.1"));
295             Netmask subnet = new NetmaskBuilder().setNetmask(new DottedQuad(stringMask)).build();
296             Address data = new AddressBuilder().setIp(noZoneIp).setSubnet(subnet).build();
297
298             defineMapping(mappingContext, IFACE_NAME, IFACE_ID, IFC_CTX_NAME);
299             whenSwInterfaceAddDelAddressThenSuccess();
300
301             customizer.writeCurrentAttributes(id, data, writeContext);
302         } catch (IllegalArgumentException e) {
303             return;
304         }
305         fail("IllegalArgumentException expected");
306
307     }
308
309     /**
310      * Test contiguous netmask length from QuadDotted notation
311      */
312     @Test
313     public void testNetmaskLength() throws Exception {
314         testSingleNetmask(1, "128.0.0.0");
315         testSingleNetmask(2, "192.0.0.0");
316         testSingleNetmask(8, "255.0.0.0");
317         testSingleNetmask(9, "255.128.0.0");
318         testSingleNetmask(16, "255.255.0.0");
319         testSingleNetmask(24, "255.255.255.0");
320     }
321
322     @Test
323     public void testNetmaskIllegal() throws Exception {
324         testSingleIllegalNetmask("");
325         testSingleIllegalNetmask(".");
326         testSingleIllegalNetmask(".255");
327         testSingleIllegalNetmask("255");
328         testSingleIllegalNetmask("255.");
329         testSingleIllegalNetmask("255.255");
330         testSingleIllegalNetmask("255.255.0");
331         testSingleIllegalNetmask("255.255.255.");
332         testSingleIllegalNetmask("255.255.255.256");
333         testSingleIllegalNetmask("0.0.0.0");
334         testSingleIllegalNetmask("10.10.10.10");
335         testSingleIllegalNetmask("255.1.255.0");
336         testSingleIllegalNetmask("255.255.255.255");
337     }
338 }