blob: b9ef48a80f66ad1d5b8c8dee11593153a3d837d9 [file] [log] [blame]
Jian Libde20bf2019-01-25 17:34:43 +09001/*
2 * Copyright 2019-present Open Networking Foundation
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 */
16package org.onosproject.k8snetworking.util;
17
18import com.fasterxml.jackson.core.JsonParseException;
19import com.fasterxml.jackson.core.JsonProcessingException;
20import com.fasterxml.jackson.databind.JsonMappingException;
21import com.fasterxml.jackson.databind.ObjectMapper;
Jian Li2cc2b632019-02-18 00:56:40 +090022import com.google.common.collect.Maps;
Jian Li85387732019-02-19 23:56:18 +090023import io.fabric8.kubernetes.client.ConfigBuilder;
24import io.fabric8.kubernetes.client.DefaultKubernetesClient;
25import io.fabric8.kubernetes.client.KubernetesClient;
26import org.apache.commons.lang3.StringUtils;
Jian Li9b199162019-02-10 18:00:35 +090027import org.apache.commons.net.util.SubnetUtils;
28import org.onlab.packet.IpAddress;
Jian Li140d8a22019-04-24 23:41:44 +090029import org.onlab.packet.TpPort;
Jian Li4aa17642019-01-30 00:01:11 +090030import org.onosproject.cfg.ConfigProperty;
31import org.onosproject.k8snetworking.api.K8sNetwork;
32import org.onosproject.k8snetworking.api.K8sNetworkService;
Jian Li85387732019-02-19 23:56:18 +090033import org.onosproject.k8snode.api.K8sApiConfig;
Jian Li3d1111e2019-02-22 02:02:13 +090034import org.onosproject.k8snode.api.K8sApiConfigService;
Jian Li4aa17642019-01-30 00:01:11 +090035import org.onosproject.k8snode.api.K8sNode;
Jian Li2cc2b632019-02-18 00:56:40 +090036import org.onosproject.k8snode.api.K8sNodeService;
Jian Li4aa17642019-01-30 00:01:11 +090037import org.onosproject.net.PortNumber;
Jian Li2cc2b632019-02-18 00:56:40 +090038import org.onosproject.net.group.DefaultGroupKey;
39import org.onosproject.net.group.GroupKey;
Jian Libde20bf2019-01-25 17:34:43 +090040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.io.IOException;
Jian Li9b199162019-02-10 18:00:35 +090044import java.util.Arrays;
45import java.util.HashSet;
Jian Li2cc2b632019-02-18 00:56:40 +090046import java.util.Map;
Jian Li140d8a22019-04-24 23:41:44 +090047import java.util.Objects;
Jian Li4aa17642019-01-30 00:01:11 +090048import java.util.Optional;
49import java.util.Set;
Jian Li9b199162019-02-10 18:00:35 +090050import java.util.stream.Collectors;
Jian Libde20bf2019-01-25 17:34:43 +090051
52import static org.onosproject.k8snetworking.api.Constants.PORT_NAME_PREFIX_CONTAINER;
53
54/**
55 * An utility that used in kubernetes networking app.
56 */
57public final class K8sNetworkingUtil {
58
59 private static final Logger log = LoggerFactory.getLogger(K8sNetworkingUtil.class);
60
Jian Li85387732019-02-19 23:56:18 +090061 private static final String COLON_SLASH = "://";
62 private static final String COLON = ":";
63
Jian Li140d8a22019-04-24 23:41:44 +090064 private static final String STR_ZERO = "0";
65 private static final String STR_ONE = "1";
66 private static final String STR_PADDING = "0000000000000000";
67 private static final int MASK_BEGIN_IDX = 0;
68 private static final int MASK_MAX_IDX = 16;
69 private static final int MASK_RADIX = 2;
70 private static final int PORT_RADIX = 16;
71
Jian Libde20bf2019-01-25 17:34:43 +090072 private K8sNetworkingUtil() {
73 }
74
75 /**
76 * Checks that whether the port is associated with container interface.
77 *
78 * @param portName port name
79 * @return true if the port is associated with container; false otherwise
80 */
81 public static boolean isContainer(String portName) {
Jian Li4aa17642019-01-30 00:01:11 +090082 return portName != null && portName.contains(PORT_NAME_PREFIX_CONTAINER);
83 }
84
85 /**
86 * Returns the tunnel port number with specified net ID and kubernetes node.
87 *
88 * @param netId network ID
89 * @param netService network service
90 * @param node kubernetes node
91 * @return tunnel port number
92 */
93 public static PortNumber tunnelPortNumByNetId(String netId,
94 K8sNetworkService netService,
95 K8sNode node) {
96 K8sNetwork.Type netType = netService.network(netId).type();
97
98 if (netType == null) {
99 return null;
100 }
101
102 return tunnelPortNumByNetType(netType, node);
103 }
104
105 /**
106 * Returns the tunnel port number with specified net type and kubernetes node.
107 *
108 * @param netType network type
109 * @param node kubernetes node
110 * @return tunnel port number
111 */
112 public static PortNumber tunnelPortNumByNetType(K8sNetwork.Type netType,
113 K8sNode node) {
114 switch (netType) {
115 case VXLAN:
116 return node.vxlanPortNum();
117 case GRE:
118 return node.grePortNum();
119 case GENEVE:
120 return node.genevePortNum();
121 default:
122 return null;
123 }
124 }
125
126 /**
127 * Obtains the property value with specified property key name.
128 *
129 * @param properties a collection of properties
130 * @param name key name
131 * @return mapping value
132 */
133 public static String getPropertyValue(Set<ConfigProperty> properties,
134 String name) {
135 Optional<ConfigProperty> property =
136 properties.stream().filter(p -> p.name().equals(name)).findFirst();
137 return property.map(ConfigProperty::value).orElse(null);
Jian Libde20bf2019-01-25 17:34:43 +0900138 }
139
140 /**
141 * Prints out the JSON string in pretty format.
142 *
143 * @param mapper Object mapper
144 * @param jsonString JSON string
145 * @return pretty formatted JSON string
146 */
147 public static String prettyJson(ObjectMapper mapper, String jsonString) {
148 try {
149 Object jsonObject = mapper.readValue(jsonString, Object.class);
150 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
151 } catch (JsonParseException e) {
152 log.debug("JsonParseException caused by {}", e);
153 } catch (JsonMappingException e) {
154 log.debug("JsonMappingException caused by {}", e);
155 } catch (JsonProcessingException e) {
156 log.debug("JsonProcessingException caused by {}", e);
157 } catch (IOException e) {
158 log.debug("IOException caused by {}", e);
159 }
160 return null;
161 }
Jian Li9b199162019-02-10 18:00:35 +0900162
163 /**
164 * Obtains valid IP addresses of the given subnet.
165 *
166 * @param cidr CIDR
167 * @return set of IP addresses
168 */
169 public static Set<IpAddress> getSubnetIps(String cidr) {
170 SubnetUtils utils = new SubnetUtils(cidr);
Jian Li7970b712019-05-03 20:58:21 +0900171 utils.setInclusiveHostCount(false);
Jian Li9b199162019-02-10 18:00:35 +0900172 SubnetUtils.SubnetInfo info = utils.getInfo();
173 Set<String> allAddresses =
174 new HashSet<>(Arrays.asList(info.getAllAddresses()));
175
176 if (allAddresses.size() > 2) {
Jian Li7970b712019-05-03 20:58:21 +0900177 allAddresses.remove(info.getLowAddress());
178 allAddresses.remove(info.getHighAddress());
Jian Li9b199162019-02-10 18:00:35 +0900179 }
180
181 return allAddresses.stream()
182 .map(IpAddress::valueOf).collect(Collectors.toSet());
183 }
Jian Li85387732019-02-19 23:56:18 +0900184
185 /**
Jian Li2cc2b632019-02-18 00:56:40 +0900186 * Obtains flow group key from the given id.
187 *
188 * @param groupId flow group identifier
189 * @return flow group key
190 */
191 public static GroupKey getGroupKey(int groupId) {
192 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
193 }
194
195 /**
Jian Li85387732019-02-19 23:56:18 +0900196 * Generates endpoint URL by referring to scheme, ipAddress and port.
197 *
198 * @param scheme scheme
199 * @param ipAddress IP address
200 * @param port port number
201 * @return generated endpoint URL
202 */
203 public static String endpoint(K8sApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
204 StringBuilder endpoint = new StringBuilder();
205 String protocol = StringUtils.lowerCase(scheme.name());
206
207 endpoint.append(protocol);
208 endpoint.append(COLON_SLASH);
209 endpoint.append(ipAddress.toString());
210 endpoint.append(COLON);
211 endpoint.append(port);
212
213 return endpoint.toString();
214 }
215
216 /**
217 * Generates endpoint URL by referring to scheme, ipAddress and port.
218 *
219 * @param apiConfig kubernetes API config
220 * @return generated endpoint URL
221 */
222 public static String endpoint(K8sApiConfig apiConfig) {
223 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
224 }
225
226 /**
227 * Obtains workable kubernetes client.
228 *
229 * @param config kubernetes API config
230 * @return kubernetes client
231 */
232 public static KubernetesClient k8sClient(K8sApiConfig config) {
233 if (config == null) {
234 log.warn("Kubernetes API server config is empty.");
235 return null;
236 }
237
238 String endpoint = endpoint(config);
239
240 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
241
242 if (config.scheme() == K8sApiConfig.Scheme.HTTPS) {
243 configBuilder.withTrustCerts(true)
244 .withOauthToken(config.token())
245 .withCaCertData(config.caCertData())
246 .withClientCertData(config.clientCertData())
247 .withClientKeyData(config.clientKeyData());
248 }
249
250 return new DefaultKubernetesClient(configBuilder.build());
251 }
Jian Li3d1111e2019-02-22 02:02:13 +0900252
253 /**
254 * Obtains workable kubernetes client.
255 *
256 * @param service kubernetes API service
257 * @return kubernetes client
258 */
259 public static KubernetesClient k8sClient(K8sApiConfigService service) {
260 K8sApiConfig config =
261 service.apiConfigs().stream().findAny().orElse(null);
262 if (config == null) {
263 log.error("Failed to find valid kubernetes API configuration.");
264 return null;
265 }
266
267 KubernetesClient client = k8sClient(config);
268
269 if (client == null) {
270 log.error("Failed to connect to kubernetes API server.");
271 return null;
272 }
273
274 return client;
275 }
Jian Li2cc2b632019-02-18 00:56:40 +0900276
277 /**
278 * Obtains the kubernetes node IP and kubernetes network gateway IP map.
279 *
280 * @param nodeService kubernetes node service
281 * @param networkService kubernetes network service
282 * @return kubernetes node IP and kubernetes network gateway IP map
283 */
284 public static Map<String, String> nodeIpGatewayIpMap(K8sNodeService nodeService,
285 K8sNetworkService networkService) {
286 Map<String, String> ipMap = Maps.newConcurrentMap();
287
288 nodeService.completeNodes().forEach(n -> {
289 K8sNetwork network = networkService.network(n.hostname());
290 if (network != null) {
291 ipMap.put(n.dataIp().toString(), network.gatewayIp().toString());
292 }
293 });
294
295 return ipMap;
296 }
Jian Li004526d2019-02-25 16:26:27 +0900297
298 /**
Jian Li73d3b6a2019-07-08 18:07:53 +0900299 * Returns a shifted IP address.
300 *
301 * @param ipAddress IP address to be shifted
302 * @param shiftPrefix A IP prefix used in shifted IP address
303 * @return shifted Ip address
304 */
305 public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
306 String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
307 return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
308 }
309
310 /**
Jian Li140d8a22019-04-24 23:41:44 +0900311 * Returns an unshifted IP address.
Jian Li004526d2019-02-25 16:26:27 +0900312 *
Jian Li140d8a22019-04-24 23:41:44 +0900313 * @param ipAddress IP address to be unshifted
314 * @param ipPrefix IP prefix which to be used for unshifting
315 * @param cidr a POD network CIDR
316 * @return unshifted IP address
Jian Li004526d2019-02-25 16:26:27 +0900317 */
Jian Li140d8a22019-04-24 23:41:44 +0900318 public static String unshiftIpDomain(String ipAddress,
319 String ipPrefix,
320 String cidr) {
Jian Li004526d2019-02-25 16:26:27 +0900321
Jian Li140d8a22019-04-24 23:41:44 +0900322 String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
323 return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
324 }
Jian Li004526d2019-02-25 16:26:27 +0900325
Jian Li140d8a22019-04-24 23:41:44 +0900326 /**
327 * Returns the B class IP prefix of the given CIDR.
328 *
329 * @param cidr CIDR
330 * @return IP prefix
331 */
332 public static String getBclassIpPrefixFromCidr(String cidr) {
333 if (cidr == null) {
334 return null;
335 }
336 return cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
337 }
Jian Li004526d2019-02-25 16:26:27 +0900338
Jian Li140d8a22019-04-24 23:41:44 +0900339 /**
340 * Returns the A class IP prefix of the given CIDR.
341 *
342 * @param cidr CIDR
343 * @return IP prefix
344 */
345 public static String getAclassIpPrefixFromCidr(String cidr) {
346 if (cidr == null) {
347 return null;
348 }
349 return cidr.split("\\.")[0];
350 }
351
352 /**
353 * Returns the map of port range.
354 *
355 * @param portMin minimum port number
356 * @param portMax maximum port number
357 * @return map of port range
358 */
359 public static Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
360
361 boolean processing = true;
362 int start = portMin;
363 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
364 while (processing) {
365 String minStr = Integer.toBinaryString(start);
366 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
367
368 int mask = testMasks(binStrMinPadded, start, portMax);
369 int maskStart = binLower(binStrMinPadded, mask);
370 int maskEnd = binHigher(binStrMinPadded, mask);
371
372 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
373 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
374 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
375
376 start = maskEnd + 1;
377 if (start > portMax) {
378 processing = false;
379 }
380 }
381
382 return portMaskMap;
383 }
384
385 private static int binLower(String binStr, int bits) {
386 StringBuilder outBin = new StringBuilder(
387 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
388 for (int i = 0; i < bits; i++) {
389 outBin.append(STR_ZERO);
390 }
391
392 return Integer.parseInt(outBin.toString(), MASK_RADIX);
393 }
394
395 private static int binHigher(String binStr, int bits) {
396 StringBuilder outBin = new StringBuilder(
397 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
398 for (int i = 0; i < bits; i++) {
399 outBin.append(STR_ONE);
400 }
401
402 return Integer.parseInt(outBin.toString(), MASK_RADIX);
403 }
404
405 private static int testMasks(String binStr, int start, int end) {
406 int mask = MASK_BEGIN_IDX;
407 for (; mask <= MASK_MAX_IDX; mask++) {
408 int maskStart = binLower(binStr, mask);
409 int maskEnd = binHigher(binStr, mask);
410 if (maskStart < start || maskEnd > end) {
411 return mask - 1;
412 }
413 }
414
415 return mask;
416 }
417
418 private static String getMask(int bits) {
419 switch (bits) {
420 case 0: return "ffff";
421 case 1: return "fffe";
422 case 2: return "fffc";
423 case 3: return "fff8";
424 case 4: return "fff0";
425 case 5: return "ffe0";
426 case 6: return "ffc0";
427 case 7: return "ff80";
428 case 8: return "ff00";
429 case 9: return "fe00";
430 case 10: return "fc00";
431 case 11: return "f800";
432 case 12: return "f000";
433 case 13: return "e000";
434 case 14: return "c000";
435 case 15: return "8000";
436 case 16: return "0000";
437 default: return null;
438 }
Jian Li004526d2019-02-25 16:26:27 +0900439 }
Jian Libde20bf2019-01-25 17:34:43 +0900440}