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