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