HONEYCOMB-83: Add Gre Feature within HC
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / interfacesstate / InterfaceUtils.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.interfacesstate;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static java.util.Objects.requireNonNull;
21
22 import io.fd.honeycomb.translate.ModificationCache;
23 import io.fd.honeycomb.translate.read.ReadFailedException;
24 import io.fd.honeycomb.translate.util.RWUtils;
25 import io.fd.honeycomb.translate.v3po.util.TranslateUtils;
26 import java.math.BigInteger;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.concurrent.CompletionStage;
30 import java.util.stream.Collector;
31 import java.util.stream.Collectors;
32 import javax.annotation.Nonnull;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Gauge64;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanGpeTunnel;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.GreTunnel;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.openvpp.jvpp.VppBaseCallException;
44 import org.openvpp.jvpp.dto.SwInterfaceDetails;
45 import org.openvpp.jvpp.dto.SwInterfaceDetailsReplyDump;
46 import org.openvpp.jvpp.dto.SwInterfaceDump;
47 import org.openvpp.jvpp.future.FutureJVpp;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public final class InterfaceUtils {
52     private static final Logger LOG = LoggerFactory.getLogger(InterfaceUtils.class);
53
54     private static final Gauge64 vppLinkSpeed0 = new Gauge64(BigInteger.ZERO);
55     private static final Gauge64 vppLinkSpeed1 = new Gauge64(BigInteger.valueOf(10L * 1000000));
56     private static final Gauge64 vppLinkSpeed2 = new Gauge64(BigInteger.valueOf(100L * 1000000));
57     private static final Gauge64 vppLinkSpeed4 = new Gauge64(BigInteger.valueOf(1000L * 1000000));
58     private static final Gauge64 vppLinkSpeed8 = new Gauge64(BigInteger.valueOf(10000L * 1000000));
59     private static final Gauge64 vppLinkSpeed16 = new Gauge64(BigInteger.valueOf(40000L * 1000000));
60     private static final Gauge64 vppLinkSpeed32 = new Gauge64(BigInteger.valueOf(100000L * 1000000));
61
62     private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
63
64     private static final int PHYSICAL_ADDRESS_LENGTH = 6;
65
66     private static final Collector<SwInterfaceDetails, ?, SwInterfaceDetails> SINGLE_ITEM_COLLECTOR =
67         RWUtils.singleItemCollector();
68
69     private InterfaceUtils() {
70         throw new UnsupportedOperationException("This utility class cannot be instantiated");
71     }
72
73     /**
74      * Convert VPP's link speed bitmask to Yang type. 1 = 10M, 2 = 100M, 4 = 1G, 8 = 10G, 16 = 40G, 32 = 100G
75      *
76      * @param vppLinkSpeed Link speed in bitmask format from VPP.
77      * @return Converted value from VPP link speed
78      */
79     public static Gauge64 vppInterfaceSpeedToYang(byte vppLinkSpeed) {
80         switch (vppLinkSpeed) {
81             case 1:
82                 return vppLinkSpeed1;
83             case 2:
84                 return vppLinkSpeed2;
85             case 4:
86                 return vppLinkSpeed4;
87             case 8:
88                 return vppLinkSpeed8;
89             case 16:
90                 return vppLinkSpeed16;
91             case 32:
92                 return vppLinkSpeed32;
93             default:
94                 return vppLinkSpeed0;
95         }
96     }
97
98     private static final void appendHexByte(final StringBuilder sb, final byte b) {
99         final int v = b & 0xFF;
100         sb.append(HEX_CHARS[v >>> 4]);
101         sb.append(HEX_CHARS[v & 15]);
102     }
103
104     // TODO rename and move to V3poUtils
105
106     /**
107      * Reads first 6 bytes of supplied byte array and converts to string as Yang dictates <p> Replace later with
108      * https://git.opendaylight.org/gerrit/#/c/34869/10/model/ietf/ietf-type- util/src/main/
109      * java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtil.java
110      *
111      * @param vppPhysAddress byte array of bytes in big endian order, constructing the network IF physical address.
112      * @return String like "aa:bb:cc:dd:ee:ff"
113      * @throws NullPointerException     if vppPhysAddress is null
114      * @throws IllegalArgumentException if vppPhysAddress.length < 6
115      */
116     public static String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress) {
117         return vppPhysAddrToYang(vppPhysAddress, 0);
118     }
119
120     public static String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress, final int startIndex) {
121         Objects.requireNonNull(vppPhysAddress, "Empty physical address bytes");
122         final int endIndex = startIndex + PHYSICAL_ADDRESS_LENGTH;
123         checkArgument(endIndex <= vppPhysAddress.length,
124             "Invalid physical address size (%s) for given startIndex (%s), expected >= %s", vppPhysAddress.length,
125             startIndex, endIndex);
126         return printHexBinary(vppPhysAddress, startIndex, endIndex);
127     }
128
129     public static String printHexBinary(@Nonnull final byte[] bytes) {
130         Objects.requireNonNull(bytes, "bytes array should not be null");
131         return printHexBinary(bytes, 0, bytes.length);
132     }
133
134     private static String printHexBinary(@Nonnull final byte[] bytes, final int startIndex, final int endIndex) {
135         StringBuilder str = new StringBuilder();
136
137         appendHexByte(str, bytes[startIndex]);
138         for (int i = startIndex + 1; i < endIndex; i++) {
139             str.append(":");
140             appendHexByte(str, bytes[i]);
141         }
142
143         return str.toString();
144     }
145
146     /**
147      * VPP's interface index is counted from 0, whereas ietf-interface's if-index is from 1. This function converts from
148      * VPP's interface index to YANG's interface index.
149      *
150      * @param vppIfIndex the sw interface index VPP reported.
151      * @return VPP's interface index incremented by one
152      */
153     public static int vppIfIndexToYang(int vppIfIndex) {
154         return vppIfIndex + 1;
155     }
156
157     /**
158      * This function does the opposite of what {@link #vppIfIndexToYang(int)} does.
159      *
160      * @param yangIfIndex if-index from ietf-interfaces.
161      * @return VPP's representation of the if-index
162      */
163     public static int yangIfIndexToVpp(int yangIfIndex) {
164         checkArgument(yangIfIndex >= 1, "YANG if-index has invalid value %s", yangIfIndex);
165         return yangIfIndex - 1;
166     }
167
168
169     /**
170      * Queries VPP for interface description given interface key.
171      *
172      * @param futureJvpp VPP Java Future API
173      * @param id         InstanceIdentifier, which is passed in ReadFailedException
174      * @param name       interface name
175      * @param index      VPP index of the interface
176      * @param ctx        per-tx scope context containing cached dump with all the interfaces. If the cache is not
177      *                   available or outdated, another dump will be performed.
178      * @return SwInterfaceDetails DTO or null if interface was not found
179      * @throws IllegalArgumentException If interface cannot be found
180      * @throws ReadFailedException      If read operation had failed
181      */
182     @Nonnull
183     public static SwInterfaceDetails getVppInterfaceDetails(@Nonnull final FutureJVpp futureJvpp,
184                                                             @Nonnull final InstanceIdentifier<?> id,
185                                                             @Nonnull final String name, final int index,
186                                                             @Nonnull final ModificationCache ctx)
187         throws ReadFailedException {
188         requireNonNull(futureJvpp, "futureJvpp should not be null");
189         requireNonNull(name, "name should not be null");
190         requireNonNull(ctx, "ctx should not be null");
191
192         final SwInterfaceDump request = new SwInterfaceDump();
193         request.nameFilter = name.getBytes();
194         request.nameFilterValid = 1;
195
196         final Map<Integer, SwInterfaceDetails> allInterfaces = InterfaceCustomizer.getCachedInterfaceDump(ctx);
197
198         // Returned cached if available
199         if (allInterfaces.containsKey(index)) {
200             return allInterfaces.get(index);
201         }
202
203         SwInterfaceDetailsReplyDump ifaces;
204         try {
205             CompletionStage<SwInterfaceDetailsReplyDump> requestFuture = futureJvpp.swInterfaceDump(request);
206             ifaces = TranslateUtils.getReplyForRead(requestFuture.toCompletableFuture(), id);
207             if (null == ifaces || null == ifaces.swInterfaceDetails || ifaces.swInterfaceDetails.isEmpty()) {
208                 request.nameFilterValid = 0;
209
210                 LOG.warn("VPP returned null instead of interface by key {} and its not cached", name);
211                 LOG.warn("Iterating through all the interfaces to find interface: {}", name);
212
213                 // Or else just perform full dump and do inefficient filtering
214                 requestFuture = futureJvpp.swInterfaceDump(request);
215                 ifaces = TranslateUtils.getReplyForRead(requestFuture.toCompletableFuture(), id);
216
217                 // Update the cache
218                 allInterfaces.clear();
219                 allInterfaces
220                     .putAll(ifaces.swInterfaceDetails.stream().collect(Collectors.toMap(d -> d.swIfIndex, d -> d)));
221
222                 if (allInterfaces.containsKey(index)) {
223                     return allInterfaces.get(index);
224                 }
225                 throw new IllegalArgumentException("Unable to find interface " + name);
226             }
227         } catch (VppBaseCallException e) {
228             LOG.warn("getVppInterfaceDetails for id :{} and name :{} failed with exception :", id, name, e);
229             throw new ReadFailedException(id, e);
230         }
231
232         // SwInterfaceDump's name filter does prefix match, so we need additional filtering:
233         final SwInterfaceDetails iface =
234             ifaces.swInterfaceDetails.stream().filter(d -> d.swIfIndex == index).collect(SINGLE_ITEM_COLLECTOR);
235         allInterfaces.put(index, iface); // update the cache
236         return iface;
237     }
238
239     /**
240      * Determine interface type based on its VPP name (relying on VPP's interface naming conventions)
241      *
242      * @param interfaceName VPP generated interface name
243      * @return Interface type
244      */
245     @Nonnull
246     public static Class<? extends InterfaceType> getInterfaceType(@Nonnull final String interfaceName) {
247         if (interfaceName.startsWith("tap")) {
248             return Tap.class;
249         }
250
251         if (interfaceName.startsWith("vxlan_gpe")) {
252             return VxlanGpeTunnel.class;
253         }
254
255         if (interfaceName.startsWith("vxlan")) {
256             return VxlanTunnel.class;
257         }
258
259         if (interfaceName.startsWith("gre")) {
260             return GreTunnel.class;
261         }
262
263         if (interfaceName.startsWith("VirtualEthernet")) {
264             return VhostUser.class;
265         }
266
267         return EthernetCsmacd.class;
268     }
269
270     /**
271      * Check interface type. Uses interface details from VPP to determine. Uses {@link
272      * #getVppInterfaceDetails(FutureJVpp, InstanceIdentifier, String, int, ModificationCache)} internally so tries to
273      * utilize cache before asking VPP.
274      */
275     static boolean isInterfaceOfType(@Nonnull final FutureJVpp jvpp,
276                                      @Nonnull final ModificationCache cache,
277                                      @Nonnull final InstanceIdentifier<?> id,
278                                      final int index,
279                                      @Nonnull final Class<? extends InterfaceType> ifcType) throws ReadFailedException {
280         final String name = id.firstKeyOf(Interface.class).getName();
281         final SwInterfaceDetails vppInterfaceDetails =
282             getVppInterfaceDetails(jvpp, id, name, index, cache);
283
284         return isInterfaceOfType(ifcType, vppInterfaceDetails);
285     }
286
287     static boolean isInterfaceOfType(final Class<? extends InterfaceType> ifcType,
288                                      final SwInterfaceDetails cachedDetails) {
289         return ifcType.equals(getInterfaceType(TranslateUtils.toString(cachedDetails.interfaceName)));
290     }
291 }