Handle nullable lists in fibmanager
[netvirt.git] / fibmanager / api / src / main / java / org / opendaylight / netvirt / fibmanager / api / FibHelper.java
1 /*
2  * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netvirt.fibmanager.api;
10
11 import static java.util.Comparator.comparing;
12 import static java.util.stream.Collectors.toList;
13
14 import java.math.BigInteger;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.stream.Collectors;
22 import javax.annotation.Nullable;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePathsBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePathsKey;
35 import org.opendaylight.yangtools.yang.binding.DataObject;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
38
39 public final class FibHelper {
40
41     private FibHelper() { }
42
43     public static RoutePaths buildRoutePath(String nextHop, Long label) {
44         RoutePathsBuilder builder = new RoutePathsBuilder()
45                 .withKey(new RoutePathsKey(nextHop))
46                 .setNexthopAddress(nextHop);
47         if (label != null) {
48             builder.setLabel(label);
49         }
50         return builder.build();
51     }
52
53     public static VrfEntryBuilder getVrfEntryBuilder(String prefix, RouteOrigin origin, @Nullable String parentVpnRd) {
54         return new VrfEntryBuilder().withKey(new VrfEntryKey(prefix)).setDestPrefix(prefix)
55                 .setOrigin(origin.getValue()).setParentVpnRd(parentVpnRd);
56     }
57
58     public static VrfEntryBuilder getVrfEntryBuilder(String prefix, List<RoutePaths> routePaths,
59             RouteOrigin origin, @Nullable String parentVpnRd) {
60         return new VrfEntryBuilder().withKey(new VrfEntryKey(prefix)).setDestPrefix(prefix)
61                 .setRoutePaths(routePaths).setOrigin(origin.getValue()).setParentVpnRd(parentVpnRd);
62     }
63
64     public static VrfEntryBuilder getVrfEntryBuilder(String prefix, long label, String nextHop, RouteOrigin origin,
65             @Nullable String parentVpnRd) {
66         if (nextHop != null) {
67             RoutePaths routePath = buildRoutePath(nextHop, label);
68             return getVrfEntryBuilder(prefix, Collections.singletonList(routePath), origin, parentVpnRd);
69         } else {
70             return getVrfEntryBuilder(prefix, origin, parentVpnRd);
71         }
72     }
73
74     public static VrfEntryBuilder getVrfEntryBuilder(VrfEntry vrfEntry, long label,
75             List<String> nextHopList, RouteOrigin origin, @Nullable String parentvpnRd) {
76         List<RoutePaths> routePaths =
77                 nextHopList.stream().map(nextHop -> buildRoutePath(nextHop, label))
78                         .collect(toList());
79         return getVrfEntryBuilder(vrfEntry.getDestPrefix(), routePaths, origin, parentvpnRd);
80     }
81
82     public static InstanceIdentifier<RoutePaths> buildRoutePathId(String rd, String prefix, String nextHop) {
83         InstanceIdentifierBuilder<RoutePaths> idBuilder =
84                 InstanceIdentifier.builder(FibEntries.class)
85                         .child(VrfTables.class, new VrfTablesKey(rd))
86                         .child(VrfEntry.class, new VrfEntryKey(prefix))
87                         .child(RoutePaths.class, new RoutePathsKey(nextHop));
88         return idBuilder.build();
89     }
90
91     public static boolean isControllerManagedRoute(RouteOrigin routeOrigin) {
92         return routeOrigin == RouteOrigin.STATIC
93                 || routeOrigin == RouteOrigin.CONNECTED
94                 || routeOrigin == RouteOrigin.LOCAL
95                 || routeOrigin == RouteOrigin.INTERVPN;
96     }
97
98     public static boolean isControllerManagedNonInterVpnLinkRoute(RouteOrigin routeOrigin) {
99         return routeOrigin == RouteOrigin.STATIC
100                 || routeOrigin == RouteOrigin.CONNECTED
101                 || routeOrigin == RouteOrigin.LOCAL;
102     }
103
104     public static boolean isControllerManagedVpnInterfaceRoute(RouteOrigin routeOrigin) {
105         return routeOrigin == RouteOrigin.STATIC
106                 || routeOrigin == RouteOrigin.LOCAL;
107     }
108
109     public static boolean isControllerManagedNonSelfImportedRoute(RouteOrigin routeOrigin) {
110         return routeOrigin != RouteOrigin.SELF_IMPORTED;
111     }
112
113     public static void sortIpAddress(List<RoutePaths> routePathList) {
114         if (routePathList != null) {
115             routePathList.sort(comparing(RoutePaths::getNexthopAddress));
116         }
117     }
118
119     public static InstanceIdentifier<RoutePaths> getRoutePathsIdentifier(String rd, String prefix, String nh) {
120         return InstanceIdentifier.builder(FibEntries.class)
121                 .child(VrfTables.class,new VrfTablesKey(rd)).child(VrfEntry.class,new VrfEntryKey(prefix))
122                 .child(RoutePaths.class, new RoutePathsKey(nh)).build();
123     }
124
125     public static List<String> getNextHopListFromRoutePaths(final VrfEntry vrfEntry) {
126         List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
127         if (routePaths == null || routePaths.isEmpty()) {
128             return new ArrayList<>();
129         }
130         return routePaths.stream()
131                 .map(RoutePaths::getNexthopAddress)
132                 .collect(Collectors.toList());
133     }
134
135     public static com.google.common.base.Optional<VrfEntry> getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
136         InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
137             .child(VrfTables.class, new VrfTablesKey(rd))
138             .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
139         return read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
140     }
141
142     private static <T extends DataObject> com.google.common.base.Optional<T> read(DataBroker broker,
143             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
144         try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
145             return tx.read(datastoreType, path).get();
146         } catch (InterruptedException | ExecutionException e) {
147             throw new RuntimeException(e);
148         }
149     }
150
151     /** get true if this prefix is an IPv4 version, false otherwise.
152      * @param prefix the prefix as (x.x.x.x/nn) to find if it is in IP version 4
153      * @return true if it is an IPv4 or false if it is not.
154      */
155     public static boolean isIpv4Prefix(String prefix) {
156         if (prefix == null || prefix.length() < 7) {
157             return false;
158         }
159         try {
160             String ip = getIpFromPrefix(prefix);
161             java.net.Inet4Address.getByName(ip);
162         } catch (SecurityException | UnknownHostException | ClassCastException e) {
163             return false;
164         }
165         return true;
166     }
167
168     /** get true if this prefix is an IPv6 version, false otherwise.
169      * @param prefix the prefix as ( x:x:x::/nn) to find if it is in IP version 6
170      * @return true if it is an IPv4 or false if it is not.
171      */
172     public static boolean isIpv6Prefix(String prefix) {
173         if (prefix == null || prefix.length() < 2) {
174             return false;
175         }
176         try {
177             String ip = getIpFromPrefix(prefix);
178             java.net.Inet6Address.getByName(ip);
179         } catch (SecurityException | UnknownHostException | ClassCastException e) {
180             return false;
181         }
182         return true;
183     }
184
185     /**get String format IP from prefix as x.x.....x/nn.
186      * @param prefix the prefix as IPv4 or IPv6 as x.....x/nn
187      * @return prefix if "/" is unfindable or the IP only as x.x...x from x.x......x/nn
188      */
189     @Nullable
190     public static String getIpFromPrefix(String prefix) {
191         if (prefix == null || prefix.length() < 2) {
192             return null;
193         }
194         String rep = prefix;
195         String[] prefixValues = prefix.split("/");
196         if (prefixValues.length > 0) {
197             rep = prefixValues[0];
198         }
199         return rep;
200     }
201
202     /**Return true if this prefix or subnet is belonging the specified subnetwork.
203      * @param prefixToTest the prefix which could be in the subnet
204      * @param subnet the subnet that have to contain the prefixToTest to return true
205      * @return true if the param subnet contained the prefixToTest false otherwise
206      */
207     public static boolean isBelongingPrefix(String prefixToTest, String subnet) {
208         return doesPrefixBelongToSubnet(prefixToTest, subnet, true);
209     }
210
211     /**Return true if this prefix or subnet is belonging the specified subnetwork.
212      * @param prefixToTest the prefix which could be in the subnet
213      * @param subnet the subnet that have to contain the prefixToTest to return true
214      * @param exactMatch boolean set to true if exact match is expected
215      * @return true if the param subnet contained the prefixToTest false otherwise
216      */
217     public static boolean doesPrefixBelongToSubnet(String prefixToTest, String subnet, boolean exactMatch) {
218         if (prefixToTest == null || prefixToTest.length() < 7 || subnet == null || subnet.length() < 7) {
219             return false;
220         }
221         if (isIpv4Prefix(prefixToTest) && isIpv4Prefix(subnet)
222                 || isIpv6Prefix(prefixToTest) && isIpv6Prefix(subnet)) {
223
224             int ipVersion = 4;
225             if (isIpv6Prefix(prefixToTest)) {
226                 ipVersion = 6;
227             }
228
229             String ipPref = getIpFromPrefix(prefixToTest);
230             String ipSub = getIpFromPrefix(subnet);
231             String maskSubString = subnet.substring(subnet.indexOf("/") + 1);
232             String maskPrefString;
233             if (prefixToTest.contains("/")) {
234                 maskPrefString = prefixToTest.substring(prefixToTest.indexOf("/") + 1);
235             } else if (ipVersion == 4) {
236                 maskPrefString = "32";
237             } else {
238                 maskPrefString = "128";
239             }
240
241             try {
242                 int maskPref = Integer.parseInt(maskPrefString);
243                 int maskSub = Integer.parseInt(maskSubString);
244                 if (exactMatch && maskPref != maskSub) {
245                  /*because the mask must be exactly the same between them, the return type is false. This behavior could
246                   * be changed to ignored it in including a boolean options to force or not the same mask control*/
247                     return false;
248                 }
249                 BigInteger maskSubBig = getMaskNetwork(ipVersion, maskSub);
250                 byte[] byteIpSub = InetAddress.getByName(ipSub).getAddress();
251                 byte[] byteIpPref = InetAddress.getByName(ipPref).getAddress();
252                 BigInteger netFromIpSub = packBigInteger(byteIpSub).and(maskSubBig);
253                 BigInteger netFromIpPref = packBigInteger(byteIpPref).and(maskSubBig);
254                 return netFromIpSub.compareTo(netFromIpPref) == 0;
255             } catch (NumberFormatException | UnknownHostException e) {
256                 return false;
257             }
258         }
259         return false;
260     }
261
262     /**This method converts a byte[] to a BigInteger value.
263      * @param bytes the byte[] of IP
264      * @return (big) integer value of IP_bytes
265      */
266     public static BigInteger packBigInteger(byte[] bytes) {
267         BigInteger val = BigInteger.valueOf(0);
268         for (byte b : bytes) {
269             val = val.shiftLeft(8);
270             val = val.add(BigInteger.valueOf(b & 0xff));
271         }
272         return val;
273     }
274
275     /**This methode return the bytes representation from an IP.
276      * @param ipBigInteger value integer of IP to convert
277      * @param isIpv4 if is ipv4 setup to true if ipv6 setup to false
278      * @return byte[] which contained the representation of bytes from th ip value
279      */
280     public static byte[] unpackBigInteger(BigInteger ipBigInteger, boolean isIpv4) {
281         int sizeBloc = 4;
282         if (!isIpv4) {
283             sizeBloc = 128 / 8;// if ipv6 size of dataIP is 128 bits
284         }
285         byte[] res = new byte[sizeBloc];
286         for (int i = 0 ; i < sizeBloc ; i++) {
287             BigInteger bigInt = ipBigInteger.shiftRight(i * 8);
288             bigInt = bigInt.and(BigInteger.valueOf(0xFF));
289             res[sizeBloc - 1 - i] = bigInt.byteValue();
290         }
291         return res;
292     }
293
294     /**get the bits cache mask of net for a ip version type.
295      * @param ipVersion version of ip must be 4 or 6
296      * @param mask the lengh of the mask of net as 24 from this representation 10.1.1.0/24 or 64 for 2001::1/64
297      * @return the bit mask of net ex: x.x.x.x/24 return a BigInteger == 0xFFFFFFotherwise null if any error
298      */
299     @Nullable
300     public static BigInteger getMaskNetwork(int ipVersion, int mask) {
301         int lenghBitsIp = 0;
302         if (ipVersion == 6) {
303             lenghBitsIp = 128 - 1;
304         } else if (ipVersion == 4) {
305             lenghBitsIp = 32 - 1;
306         } else {
307             return null;//ip version is unknown
308         }
309         BigInteger bb = BigInteger.ZERO;
310         for (int i = 0 ; i < mask; i++) {
311             bb = bb.setBit(lenghBitsIp - i);
312         }
313         return bb;
314     }
315 }