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