95b07743fe5a546cde2558a78d68a255feed75bc
[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.util;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.base.Preconditions.checkState;
22
23 import com.google.common.base.Splitter;
24 import com.google.common.net.InetAddresses;
25 import java.net.UnknownHostException;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.Future;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
32 import java.util.function.BiConsumer;
33 import java.util.stream.Collectors;
34 import javax.annotation.Nonnegative;
35 import javax.annotation.Nonnull;
36 import javax.annotation.Nullable;
37 import org.apache.commons.codec.DecoderException;
38 import org.apache.commons.codec.binary.Hex;
39 import org.apache.commons.lang3.StringUtils;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.openvpp.jvpp.VppBaseCallException;
50 import org.openvpp.jvpp.dto.JVppReply;
51
52 public final class TranslateUtils {
53
54     public static final Splitter COLON_SPLITTER = Splitter.on(':');
55     public static final int DEFAULT_TIMEOUT_IN_SECONDS = 5;
56
57     private TranslateUtils() {
58     }
59
60     public static <REP extends JVppReply<?>> REP getReplyForWrite(@Nonnull Future<REP> future,
61                                                                   @Nonnull final InstanceIdentifier<?> replyType)
62         throws VppBaseCallException, WriteTimeoutException {
63         return getReplyForWrite(future, replyType, DEFAULT_TIMEOUT_IN_SECONDS);
64     }
65
66     public static <REP extends JVppReply<?>> REP getReplyForWrite(@Nonnull Future<REP> future,
67                                                                   @Nonnull final InstanceIdentifier<?> replyType,
68                                                                   @Nonnegative final int timeoutInSeconds)
69         throws VppBaseCallException, WriteTimeoutException {
70         try {
71             return getReply(future, timeoutInSeconds);
72         } catch (TimeoutException e) {
73             throw new WriteTimeoutException(replyType, e);
74         }
75     }
76
77     public static <REP extends JVppReply<?>> REP getReplyForRead(@Nonnull Future<REP> future,
78                                                                  @Nonnull final InstanceIdentifier<?> replyType)
79         throws VppBaseCallException, ReadTimeoutException {
80         return getReplyForRead(future, replyType, DEFAULT_TIMEOUT_IN_SECONDS);
81     }
82
83     public static <REP extends JVppReply<?>> REP getReplyForRead(@Nonnull Future<REP> future,
84                                                                  @Nonnull final InstanceIdentifier<?> replyType,
85                                                                  @Nonnegative final int timeoutInSeconds)
86         throws VppBaseCallException, ReadTimeoutException {
87         try {
88             return getReply(future, timeoutInSeconds);
89         } catch (TimeoutException e) {
90             throw new ReadTimeoutException(replyType, e);
91         }
92     }
93
94     public static <REP extends JVppReply<?>> REP getReply(@Nonnull Future<REP> future)
95         throws TimeoutException, VppBaseCallException {
96         return getReply(future, DEFAULT_TIMEOUT_IN_SECONDS);
97     }
98
99     public static <REP extends JVppReply<?>> REP getReply(@Nonnull Future<REP> future,
100                                                           @Nonnegative final int timeoutInSeconds)
101         throws TimeoutException, VppBaseCallException {
102         try {
103             checkArgument(timeoutInSeconds > 0, "Timeout cannot be < 0");
104             return future.get(timeoutInSeconds, TimeUnit.SECONDS);
105         } catch (InterruptedException e) {
106             Thread.currentThread().interrupt();
107             throw new IllegalStateException("Interrupted", e);
108         } catch (ExecutionException e) {
109             // Execution exception could generally contains any exception
110             // when using exceptions instead of return codes just rethrow it for processing on corresponding place
111             if (e instanceof ExecutionException && (e.getCause() instanceof VppBaseCallException)) {
112                 throw (VppBaseCallException) (e.getCause());
113             }
114             throw new IllegalStateException(e);
115         }
116     }
117
118     public static final byte[] ipAddressToArray(IpAddress address) {
119         checkNotNull(address, "Cannot resolve null adddress");
120
121         if (isIpv6(address)) {
122             return ipv6AddressNoZoneToArray(new Ipv6AddressNoZone(address.getIpv6Address()));
123         } else {
124             return ipv4AddressNoZoneToArray(new Ipv4AddressNoZone(address.getIpv4Address()));
125         }
126     }
127
128     /**
129      * Creates address array from address part of {@link Ipv4Prefix}
130      */
131     public static byte[] ipv4AddressPrefixToArray(@Nonnull final Ipv4Prefix ipv4Prefix) {
132         checkNotNull(ipv4Prefix, "Cannot convert null prefix");
133
134         byte[] retval = new byte[4];
135         String[] address = ipv4Prefix.getValue().substring(0, ipv4Prefix.getValue().indexOf('/')).split("\\.");
136
137         for (int d = 0; d < 4; d++) {
138             retval[d] = (byte) (Short.parseShort(address[d]) & 0xff);
139         }
140         return retval;
141     }
142
143     /**
144      * Converts {@link IpAddress} to array representing {@link Ipv4Address} or {@link Ipv6Address}
145      */
146     public static byte[] ipAddressToArray(boolean isIpv6, @Nonnull IpAddress address) {
147         checkNotNull(address, "Cannot convert null Address");
148
149         if (isIpv6) {
150             return ipv6AddressNoZoneToArray(new Ipv6AddressNoZone(address.getIpv6Address()));
151         } else {
152             return ipv4AddressNoZoneToArray(new Ipv4AddressNoZone(address.getIpv4Address()));
153         }
154     }
155
156     /**
157      * Converts array bytes to {@link IpAddress}
158      */
159     @Nonnull
160     public static IpAddress arrayToIpAddress(boolean isIpv6, byte[] ip) {
161         if (isIpv6) {
162             return new IpAddress(arrayToIpv6AddressNoZone(ip));
163         } else {
164             return new IpAddress(arrayToIpv4AddressNoZone(ip));
165         }
166     }
167
168     /**
169      * Extracts {@link Ipv4Prefix} prefix
170      */
171     public static byte extractPrefix(Ipv4Prefix data) {
172         checkNotNull(data, "Cannot extract from null");
173
174         return Byte.valueOf(data.getValue().substring(data.getValue().indexOf('/') + 1));
175     }
176
177     /**
178      * Converts byte array to {@link Ipv4Prefix} with specified prefixLength
179      */
180     public static Ipv4Prefix arrayToIpv4Prefix(final byte[] address, byte prefixLength) {
181         Ipv4AddressNoZone addressPart = arrayToIpv4AddressNoZone(address);
182
183         return new Ipv4Prefix(addressPart.getValue().concat("/").concat(String.valueOf(prefixLength)));
184     }
185
186     /**
187      * Transform Ipv6 address to a byte array acceptable by VPP. VPP expects incoming byte array to be in the same order
188      * as the address.
189      *
190      * @return byte array with address bytes
191      */
192     public static byte[] ipv6AddressNoZoneToArray(@Nonnull final Ipv6AddressNoZone ipv6Addr) {
193         byte[] retval = new byte[16];
194
195         //splits address and add ommited zeros for easier parsing
196         List<String> segments = Arrays.asList(ipv6Addr.getValue().split(":"))
197                 .stream()
198                 .map(segment ->  StringUtils.repeat('0',4-segment.length())+segment)
199                 .collect(Collectors.toList());
200
201         byte index = 0;
202         for (String segment : segments) {
203
204             String firstPart =segment.substring(0, 2);
205             String secondPart = segment.substring(2);
206
207             //first part should be ommited
208             if("00".equals(firstPart)){
209                 index++;
210             }else{
211                 retval[index++] = ((byte) Short.parseShort(firstPart, 16));
212             }
213
214             retval[index++] = ((byte) Short.parseShort(secondPart, 16));
215         }
216
217         return retval;
218     }
219
220     /**
221      * Creates address array from address part of {@link Ipv6Prefix}
222      */
223     public static byte[] ipv6AddressPrefixToArray(@Nonnull final Ipv6Prefix ipv4Prefix) {
224         checkNotNull(ipv4Prefix, "Cannot convert null prefix");
225
226         return ipv6AddressNoZoneToArray(new Ipv6AddressNoZone(
227                 new Ipv6Address(ipv4Prefix.getValue().substring(0, ipv4Prefix.getValue().indexOf('/')))));
228     }
229
230     /**
231      * Extracts {@link Ipv6Prefix} prefix
232      */
233     public static byte extractPrefix(Ipv6Prefix data) {
234         checkNotNull(data, "Cannot extract from null");
235
236         return Byte.valueOf(data.getValue().substring(data.getValue().indexOf('/') + 1));
237     }
238
239     /**
240      * Converts byte array to {@link Ipv6Prefix} with specified prefixLength
241      */
242     public static Ipv6Prefix arrayToIpv6Prefix(final byte[] address, byte prefixLength) {
243         Ipv6AddressNoZone addressPart = arrayToIpv6AddressNoZone(address);
244
245         return new Ipv6Prefix(addressPart.getValue().concat("/").concat(String.valueOf(prefixLength)));
246     }
247
248     /**
249      * Parse byte array returned by VPP representing an Ipv6 address. Vpp returns IP byte arrays in reversed order.
250      *
251      * @return Ipv46ddressNoZone containing string representation of IPv6 address constructed from submitted bytes. No
252      * change in order.
253      */
254     @Nonnull
255     public static Ipv6AddressNoZone arrayToIpv6AddressNoZone(@Nonnull byte[] ip) {
256         checkArgument(ip.length == 16, "Illegal array length");
257
258         try {
259             return new Ipv6AddressNoZone(InetAddresses.toAddrString(InetAddresses.fromLittleEndianByteArray(ip)));
260         } catch (UnknownHostException e) {
261             throw new IllegalArgumentException("Unable to parse ipv6", e);
262         }
263     }
264
265
266     /**
267      * Parse byte array returned by VPP representing an Ipv6 address. Vpp returns IP byte arrays in natural order.
268      *
269      * @return Ipv46ddressNoZone containing string representation of IPv6 address constructed from submitted bytes. No
270      * change in order.
271      */
272     @Nonnull
273     public static Ipv6AddressNoZone arrayToIpv6AddressNoZoneReversed(@Nonnull byte[] ip) {
274         checkArgument(ip.length == 16, "Illegal array length");
275
276         ip = reverseBytes(ip);
277
278         try {
279             return new Ipv6AddressNoZone(InetAddresses.toAddrString(InetAddresses.fromLittleEndianByteArray(ip)));
280         } catch (UnknownHostException e) {
281             throw new IllegalArgumentException("Unable to parse ipv6", e);
282         }
283     }
284
285     /**
286      * Converts byte array to address string ,not separated with ":"
287      */
288     public static String byteArrayToMacUnseparated(byte[] address) {
289         checkArgument(address.length == 6, "Illegal array length");
290         return Hex.encodeHexString(address);
291     }
292
293     /**
294      * Converts byte array to address string ,separated with ":"
295      */
296     public static String byteArrayToMacSeparated(byte[] address) {
297         checkArgument(address.length == 6, "Illegal array length");
298
299         String unseparatedAddress = Hex.encodeHexString(address);
300         String separated = "";
301
302         for (int i = 0; i < unseparatedAddress.length(); i = i + 2) {
303             if (i == (unseparatedAddress.length() - 2)) {
304                 separated = separated + unseparatedAddress.substring(0 + i, 2 + i);
305             } else {
306                 separated = separated + unseparatedAddress.substring(0 + i, 2 + i) + ":";
307             }
308         }
309
310         return separated;
311     }
312
313     /**
314      * Converts MAC string to byte array
315      * */
316     public static byte[] macToByteArray(String mac){
317         checkNotNull(mac,"MAC cannot be null");
318
319         mac = mac.replace(":","");
320
321         try {
322             return Hex.decodeHex(mac.toCharArray());
323         } catch (DecoderException e) {
324             throw new IllegalArgumentException("Unable to convert mac",e);
325         }
326     }
327
328     /**
329      * Detects whether {@code IpAddress} is ipv6
330      */
331     public static boolean isIpv6(IpAddress address) {
332         checkNotNull(address, "Address cannot be null");
333
334                 checkState(!(address.getIpv4Address() == null && address.getIpv6Address() == null), "Invalid address");
335         return address.getIpv6Address() != null;
336     }
337
338     /**
339      * Detects whether {@code IpPrefix} is ipv6
340      */
341     public static boolean isIpv6(IpPrefix address) {
342         checkNotNull(address, "Address cannot be null");
343         checkState(!(address.getIpv4Prefix() == null && address.getIpv6Prefix() == null), "Invalid address");
344         return address.getIpv6Prefix() != null;
345     }
346
347
348     /**
349      * Transform Ipv4 address to a byte array acceptable by VPP. VPP expects incoming byte array to be in the same order
350      * as the address.
351      *
352      * @return byte array with address bytes
353      */
354     public static byte[] ipv4AddressNoZoneToArray(final Ipv4AddressNoZone ipv4Addr) {
355         return ipv4AddressNoZoneToArray(ipv4Addr.getValue());
356     }
357
358     public static byte[] ipv4AddressNoZoneToArray(final String ipv4Addr) {
359         byte[] retval = new byte[4];
360         String[] dots = ipv4Addr.split("\\.");
361
362         for (int d = 0; d < 4; d++) {
363             retval[d] = (byte) (Short.parseShort(dots[d]) & 0xff);
364         }
365         return retval;
366     }
367
368     /**
369      * Parse byte array returned by VPP representing an Ipv4 address. Vpp returns IP byte arrays in reversed order.
370      *
371      * @return Ipv4AddressNoZone containing string representation of IPv4 address constructed from submitted bytes. No
372      * change in order.
373      */
374     @Nonnull
375     public static Ipv4AddressNoZone arrayToIpv4AddressNoZone(@Nonnull byte[] ip) {
376         // VPP sends ipv4 in a 16 byte array
377         if (ip.length == 16) {
378             ip = Arrays.copyOfRange(ip, 0, 4);
379         }
380         try {
381             // Not reversing the byte array here!! because the IP coming from VPP is in reversed byte order
382             // compared to byte order it was submitted
383             return new Ipv4AddressNoZone(InetAddresses.toAddrString(InetAddresses.fromLittleEndianByteArray(ip)));
384         } catch (UnknownHostException e) {
385             throw new IllegalArgumentException("Unable to parse ipv4", e);
386         }
387     }
388
389     /**
390      * Parse byte array returned by VPP representing an Ipv4 address. Vpp returns IP byte arrays in reversed order.
391      *
392      * @return Ipv4AddressNoZone containing string representation of IPv4 address constructed from submitted bytes. No
393      * change in order.
394      */
395     @Nonnull
396     public static Ipv4AddressNoZone arrayToIpv4AddressNoZoneReversed(@Nonnull byte[] ip) {
397         // VPP sends ipv4 in a 16 byte array
398
399         if (ip.length == 16) {
400             ip = Arrays.copyOfRange(ip, 0, 4);
401         }
402
403         ip = reverseBytes(ip);
404
405         try {
406             // Not reversing the byte array here!! because the IP coming from VPP is in reversed byte order
407             // compared to byte order it was submitted
408             return new Ipv4AddressNoZone(InetAddresses.toAddrString(InetAddresses.fromLittleEndianByteArray(ip)));
409         } catch (UnknownHostException e) {
410             throw new IllegalArgumentException("Unable to parse ipv4", e);
411         }
412     }
413
414     /**
415      * Return (interned) string from byte array while removing \u0000. Strings represented as fixed length byte[] from
416      * vpp contain \u0000.
417      */
418     public static String toString(final byte[] cString) {
419         return new String(cString).replaceAll("\\u0000", "").intern();
420     }
421
422     /**
423      * Parse string represented mac address (using ":" as separator) into a byte array
424      */
425     @Nonnull
426     public static byte[] parseMac(@Nonnull final String macAddress) {
427         final List<String> parts = COLON_SPLITTER.splitToList(macAddress);
428         checkArgument(parts.size() == 6, "Mac address is expected to have 6 parts but was: %s", macAddress);
429         return parseMacLikeString(parts);
430     }
431
432     private static byte[] parseMacLikeString(final List<String> strings) {
433         return strings.stream().limit(6).map(TranslateUtils::parseHexByte).collect(
434             () -> new byte[strings.size()],
435             new BiConsumer<byte[], Byte>() {
436
437                 private int i = -1;
438
439                 @Override
440                 public void accept(final byte[] bytes, final Byte aByte) {
441                     bytes[++i] = aByte;
442                 }
443             },
444             (bytes, bytes2) -> {
445                 throw new UnsupportedOperationException("Parallel collect not supported");
446             });
447     }
448
449     public static byte parseHexByte(final String aByte) {
450         return (byte) Integer.parseInt(aByte, 16);
451     }
452
453     /**
454      * Returns 0 if argument is null or false, 1 otherwise.
455      *
456      * @param value Boolean value to be converted
457      * @return byte value equal to 0 or 1
458      */
459     public static byte booleanToByte(@Nullable final Boolean value) {
460         return value != null && value
461             ? (byte) 1
462             : (byte) 0;
463     }
464
465     /**
466      * Returns Boolean.TRUE if argument is 0, Boolean.FALSE otherwise.
467      *
468      * @param value byte value to be converted
469      * @return Boolean value
470      * @throws IllegalArgumentException if argument is neither 0 nor 1
471      */
472     @Nonnull
473     public static Boolean byteToBoolean(final byte value) {
474         if (value == 0) {
475             return Boolean.FALSE;
476         } else if (value == 1) {
477             return Boolean.TRUE;
478         }
479         throw new IllegalArgumentException(String.format("0 or 1 was expected but was %d", value));
480     }
481
482     /**
483      * Reverses bytes in the byte array
484      *
485      * @param bytes input array
486      * @return reversed array
487      */
488     public static byte[] reverseBytes(final byte[] bytes) {
489         final byte[] reversed = new byte[bytes.length];
490         int i = 1;
491         for (byte aByte : bytes) {
492             reversed[bytes.length - i++] = aByte;
493         }
494
495         return reversed;
496     }
497 }