blob: 1f4d09a965ee73f72da4ab5654ddf9c753ed6a3c [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 Lie1a5b8f2019-07-23 17:13:19 +090023import io.fabric8.kubernetes.api.model.Container;
24import io.fabric8.kubernetes.api.model.ContainerPort;
25import io.fabric8.kubernetes.api.model.Namespace;
26import io.fabric8.kubernetes.api.model.Pod;
Jian Li85387732019-02-19 23:56:18 +090027import io.fabric8.kubernetes.client.ConfigBuilder;
28import io.fabric8.kubernetes.client.DefaultKubernetesClient;
29import io.fabric8.kubernetes.client.KubernetesClient;
30import org.apache.commons.lang3.StringUtils;
Jian Li9b199162019-02-10 18:00:35 +090031import org.apache.commons.net.util.SubnetUtils;
Jian Li732c3422020-09-07 17:01:11 +090032import org.onlab.osgi.DefaultServiceDirectory;
Jian Li9b199162019-02-10 18:00:35 +090033import org.onlab.packet.IpAddress;
Jian Li4bd6f2b2019-08-16 21:39:27 +090034import org.onlab.packet.MacAddress;
Jian Li140d8a22019-04-24 23:41:44 +090035import org.onlab.packet.TpPort;
Jian Li4aa17642019-01-30 00:01:11 +090036import org.onosproject.cfg.ConfigProperty;
Jian Li4bd6f2b2019-08-16 21:39:27 +090037import org.onosproject.k8snetworking.api.DefaultK8sPort;
Jian Lie1a5b8f2019-07-23 17:13:19 +090038import org.onosproject.k8snetworking.api.K8sNamespaceService;
Jian Li4aa17642019-01-30 00:01:11 +090039import org.onosproject.k8snetworking.api.K8sNetwork;
Jian Li4bd6f2b2019-08-16 21:39:27 +090040import org.onosproject.k8snetworking.api.K8sNetworkAdminService;
Jian Li4aa17642019-01-30 00:01:11 +090041import org.onosproject.k8snetworking.api.K8sNetworkService;
Jian Lie1a5b8f2019-07-23 17:13:19 +090042import org.onosproject.k8snetworking.api.K8sPodService;
Jian Li4bd6f2b2019-08-16 21:39:27 +090043import org.onosproject.k8snetworking.api.K8sPort;
Jian Lie1a5b8f2019-07-23 17:13:19 +090044import org.onosproject.k8snetworking.api.K8sServiceService;
Jian Li85387732019-02-19 23:56:18 +090045import org.onosproject.k8snode.api.K8sApiConfig;
Jian Li3d1111e2019-02-22 02:02:13 +090046import org.onosproject.k8snode.api.K8sApiConfigService;
Jian Li732c3422020-09-07 17:01:11 +090047import org.onosproject.k8snode.api.K8sHost;
48import org.onosproject.k8snode.api.K8sHostService;
Jian Li4aa17642019-01-30 00:01:11 +090049import org.onosproject.k8snode.api.K8sNode;
Jian Li2cc2b632019-02-18 00:56:40 +090050import org.onosproject.k8snode.api.K8sNodeService;
Jian Lieab51352020-09-11 03:29:16 +090051import org.onosproject.k8snode.api.K8sRouterBridge;
Jian Li732c3422020-09-07 17:01:11 +090052import org.onosproject.k8snode.api.K8sTunnelBridge;
Jian Li4bd6f2b2019-08-16 21:39:27 +090053import org.onosproject.net.DeviceId;
Jian Li732c3422020-09-07 17:01:11 +090054import org.onosproject.net.Port;
Jian Li4aa17642019-01-30 00:01:11 +090055import org.onosproject.net.PortNumber;
Jian Li732c3422020-09-07 17:01:11 +090056import org.onosproject.net.device.DeviceService;
Jian Li2cc2b632019-02-18 00:56:40 +090057import org.onosproject.net.group.DefaultGroupKey;
58import org.onosproject.net.group.GroupKey;
Jian Libde20bf2019-01-25 17:34:43 +090059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import java.io.IOException;
Jian Li9b199162019-02-10 18:00:35 +090063import java.util.Arrays;
64import java.util.HashSet;
Jian Lia6f58382019-12-16 14:22:13 +090065import java.util.List;
Jian Li2cc2b632019-02-18 00:56:40 +090066import java.util.Map;
Jian Li140d8a22019-04-24 23:41:44 +090067import java.util.Objects;
Jian Li4aa17642019-01-30 00:01:11 +090068import java.util.Optional;
69import java.util.Set;
Jian Li9b199162019-02-10 18:00:35 +090070import java.util.stream.Collectors;
Jian Libde20bf2019-01-25 17:34:43 +090071
Jian Lie1a5b8f2019-07-23 17:13:19 +090072import static org.onosproject.k8snetworking.api.Constants.DEFAULT_NAMESPACE_HASH;
Jian Li732c3422020-09-07 17:01:11 +090073import static org.onosproject.k8snetworking.api.Constants.NORMAL_PORT_NAME_PREFIX_CONTAINER;
74import static org.onosproject.k8snetworking.api.Constants.NORMAL_PORT_PREFIX_LENGTH;
75import static org.onosproject.k8snetworking.api.Constants.PT_PORT_NAME_PREFIX_CONTAINER;
76import static org.onosproject.k8snetworking.api.Constants.PT_PORT_PREFIX_LENGTH;
Jian Li4bd6f2b2019-08-16 21:39:27 +090077import static org.onosproject.k8snetworking.api.K8sPort.State.INACTIVE;
Jian Li732c3422020-09-07 17:01:11 +090078import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
79import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Libde20bf2019-01-25 17:34:43 +090080
81/**
82 * An utility that used in kubernetes networking app.
83 */
84public final class K8sNetworkingUtil {
85
86 private static final Logger log = LoggerFactory.getLogger(K8sNetworkingUtil.class);
87
Jian Li85387732019-02-19 23:56:18 +090088 private static final String COLON_SLASH = "://";
89 private static final String COLON = ":";
90
Jian Li140d8a22019-04-24 23:41:44 +090091 private static final String STR_ZERO = "0";
92 private static final String STR_ONE = "1";
93 private static final String STR_PADDING = "0000000000000000";
94 private static final int MASK_BEGIN_IDX = 0;
95 private static final int MASK_MAX_IDX = 16;
96 private static final int MASK_RADIX = 2;
97 private static final int PORT_RADIX = 16;
98
Jian Li4bd6f2b2019-08-16 21:39:27 +090099 private static final String PORT_ID = "portId";
100 private static final String DEVICE_ID = "deviceId";
101 private static final String PORT_NUMBER = "portNumber";
102 private static final String IP_ADDRESS = "ipAddress";
103 private static final String MAC_ADDRESS = "macAddress";
104 private static final String NETWORK_ID = "networkId";
105
Jian Libde20bf2019-01-25 17:34:43 +0900106 private K8sNetworkingUtil() {
107 }
108
109 /**
110 * Checks that whether the port is associated with container interface.
111 *
112 * @param portName port name
113 * @return true if the port is associated with container; false otherwise
114 */
115 public static boolean isContainer(String portName) {
Jian Li732c3422020-09-07 17:01:11 +0900116 return portName != null && (portName.contains(NORMAL_PORT_NAME_PREFIX_CONTAINER) ||
117 portName.contains(PT_PORT_NAME_PREFIX_CONTAINER));
118 }
119
120 /**
121 * Checks that whether the compared ports exist in the source name.
122 *
123 * @param sourceName source port name
124 * @param comparedName port name to be compared
125 * @return true if the compared port name exists, false otherwise
126 */
Jian Li3bc3d5b2020-09-23 16:46:12 +0900127 public static boolean existingContainerPortByName(String sourceName, String comparedName) {
Jian Li732c3422020-09-07 17:01:11 +0900128 if (comparedName == null) {
129 return false;
130 }
131
132 if (comparedName.contains(NORMAL_PORT_NAME_PREFIX_CONTAINER)) {
133 return sourceName.contains(comparedName.substring(NORMAL_PORT_PREFIX_LENGTH));
134 }
135
136 if (comparedName.contains(PT_PORT_NAME_PREFIX_CONTAINER)) {
137 return sourceName.contains(comparedName.substring(PT_PORT_PREFIX_LENGTH));
138 }
139
140 return false;
Jian Li4aa17642019-01-30 00:01:11 +0900141 }
142
143 /**
Jian Li3bc3d5b2020-09-23 16:46:12 +0900144 * Checks that whether the compared ports exist in the source MAC address.
145 *
146 * @param sourceMac source port MAC address
147 * @param comparedMac MAC address of port to be compared
148 * @return true if the compared port MAC address exists, false otherwise
149 */
150 public static boolean existingContainerPortByMac(String sourceMac, String comparedMac) {
151 if (comparedMac == null || sourceMac == null) {
152 return false;
153 }
154
155 String shortSourceMac = sourceMac.substring(3).toUpperCase();
156 String shortComparedMac = comparedMac.substring(3).toUpperCase();
157
158 return shortSourceMac.equals(shortComparedMac);
159 }
160
161 /**
Jian Li4aa17642019-01-30 00:01:11 +0900162 * Returns the tunnel port number with specified net ID and kubernetes node.
163 *
164 * @param netId network ID
165 * @param netService network service
166 * @param node kubernetes node
167 * @return tunnel port number
168 */
169 public static PortNumber tunnelPortNumByNetId(String netId,
170 K8sNetworkService netService,
171 K8sNode node) {
172 K8sNetwork.Type netType = netService.network(netId).type();
173
174 if (netType == null) {
175 return null;
176 }
177
178 return tunnelPortNumByNetType(netType, node);
179 }
180
181 /**
182 * Returns the tunnel port number with specified net type and kubernetes node.
183 *
184 * @param netType network type
185 * @param node kubernetes node
186 * @return tunnel port number
187 */
188 public static PortNumber tunnelPortNumByNetType(K8sNetwork.Type netType,
189 K8sNode node) {
Jian Li732c3422020-09-07 17:01:11 +0900190 if (node.mode() == PASSTHROUGH) {
191 K8sHostService hostService =
192 DefaultServiceDirectory.getService(K8sHostService.class);
193 Port port = null;
194 for (K8sHost host : hostService.hosts()) {
195 if (host.nodeNames().contains(node.hostname())) {
196 for (K8sTunnelBridge bridge : host.tunBridges()) {
197 if (bridge.tunnelId() == node.segmentId()) {
198 String portName = netType.name().toLowerCase() +
199 "-" + node.segmentId();
200 port = port(bridge.deviceId(), portName);
201 }
202 }
203 }
204 }
205
206 if (port == null) {
Jian Li4aa17642019-01-30 00:01:11 +0900207 return null;
Jian Li732c3422020-09-07 17:01:11 +0900208 } else {
209 return port.number();
210 }
211
212 } else {
213 switch (netType) {
214 case VXLAN:
215 return node.vxlanPortNum();
216 case GRE:
217 return node.grePortNum();
218 case GENEVE:
219 return node.genevePortNum();
220 default:
221 return null;
222 }
Jian Li4aa17642019-01-30 00:01:11 +0900223 }
224 }
225
226 /**
Jian Li732c3422020-09-07 17:01:11 +0900227 * Obtains the port from the device with the given port name.
228 *
229 * @param deviceId device identifier
230 * @param portName port name
231 * @return port object
232 */
233 public static Port port(DeviceId deviceId, String portName) {
234 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
235 return deviceService.getPorts(deviceId).stream()
236 .filter(p -> p.isEnabled() &&
237 Objects.equals(p.annotations().value(PORT_NAME), portName))
238 .findAny().orElse(null);
239 }
240
241 /**
Jian Li4aa17642019-01-30 00:01:11 +0900242 * Obtains the property value with specified property key name.
243 *
244 * @param properties a collection of properties
245 * @param name key name
246 * @return mapping value
247 */
248 public static String getPropertyValue(Set<ConfigProperty> properties,
249 String name) {
250 Optional<ConfigProperty> property =
251 properties.stream().filter(p -> p.name().equals(name)).findFirst();
252 return property.map(ConfigProperty::value).orElse(null);
Jian Libde20bf2019-01-25 17:34:43 +0900253 }
254
255 /**
256 * Prints out the JSON string in pretty format.
257 *
258 * @param mapper Object mapper
259 * @param jsonString JSON string
260 * @return pretty formatted JSON string
261 */
262 public static String prettyJson(ObjectMapper mapper, String jsonString) {
263 try {
264 Object jsonObject = mapper.readValue(jsonString, Object.class);
265 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
266 } catch (JsonParseException e) {
Jian Li003d5692019-08-26 23:19:38 +0900267 log.debug("JsonParseException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900268 } catch (JsonMappingException e) {
Jian Li003d5692019-08-26 23:19:38 +0900269 log.debug("JsonMappingException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900270 } catch (JsonProcessingException e) {
Jian Li003d5692019-08-26 23:19:38 +0900271 log.debug("JsonProcessingException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900272 } catch (IOException e) {
Jian Li003d5692019-08-26 23:19:38 +0900273 log.debug("IOException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900274 }
275 return null;
276 }
Jian Li9b199162019-02-10 18:00:35 +0900277
278 /**
279 * Obtains valid IP addresses of the given subnet.
280 *
281 * @param cidr CIDR
282 * @return set of IP addresses
283 */
284 public static Set<IpAddress> getSubnetIps(String cidr) {
285 SubnetUtils utils = new SubnetUtils(cidr);
Jian Li7970b712019-05-03 20:58:21 +0900286 utils.setInclusiveHostCount(false);
Jian Li9b199162019-02-10 18:00:35 +0900287 SubnetUtils.SubnetInfo info = utils.getInfo();
288 Set<String> allAddresses =
289 new HashSet<>(Arrays.asList(info.getAllAddresses()));
290
291 if (allAddresses.size() > 2) {
Jian Li7970b712019-05-03 20:58:21 +0900292 allAddresses.remove(info.getLowAddress());
293 allAddresses.remove(info.getHighAddress());
Jian Li9b199162019-02-10 18:00:35 +0900294 }
295
296 return allAddresses.stream()
297 .map(IpAddress::valueOf).collect(Collectors.toSet());
298 }
Jian Li85387732019-02-19 23:56:18 +0900299
300 /**
Jian Li6d2ffbf2020-11-04 15:58:18 +0900301 * Obtains gateway IP address of the given subnet.
302 *
303 * @param cidr CIDR
304 * @return gateway IP address
305 */
306 public static IpAddress getGatewayIp(String cidr) {
307 SubnetUtils utils = new SubnetUtils(cidr);
308 utils.setInclusiveHostCount(false);
309 SubnetUtils.SubnetInfo info = utils.getInfo();
310 return IpAddress.valueOf(info.getLowAddress());
311 }
312
313 /**
Jian Li2cc2b632019-02-18 00:56:40 +0900314 * Obtains flow group key from the given id.
315 *
316 * @param groupId flow group identifier
317 * @return flow group key
318 */
319 public static GroupKey getGroupKey(int groupId) {
320 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
321 }
322
323 /**
Jian Li85387732019-02-19 23:56:18 +0900324 * Generates endpoint URL by referring to scheme, ipAddress and port.
325 *
326 * @param scheme scheme
327 * @param ipAddress IP address
328 * @param port port number
329 * @return generated endpoint URL
330 */
331 public static String endpoint(K8sApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
332 StringBuilder endpoint = new StringBuilder();
333 String protocol = StringUtils.lowerCase(scheme.name());
334
335 endpoint.append(protocol);
336 endpoint.append(COLON_SLASH);
337 endpoint.append(ipAddress.toString());
338 endpoint.append(COLON);
339 endpoint.append(port);
340
341 return endpoint.toString();
342 }
343
344 /**
345 * Generates endpoint URL by referring to scheme, ipAddress and port.
346 *
347 * @param apiConfig kubernetes API config
348 * @return generated endpoint URL
349 */
350 public static String endpoint(K8sApiConfig apiConfig) {
351 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
352 }
353
354 /**
355 * Obtains workable kubernetes client.
356 *
357 * @param config kubernetes API config
358 * @return kubernetes client
359 */
360 public static KubernetesClient k8sClient(K8sApiConfig config) {
361 if (config == null) {
362 log.warn("Kubernetes API server config is empty.");
363 return null;
364 }
365
366 String endpoint = endpoint(config);
367
368 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
369
370 if (config.scheme() == K8sApiConfig.Scheme.HTTPS) {
371 configBuilder.withTrustCerts(true)
372 .withOauthToken(config.token())
373 .withCaCertData(config.caCertData())
374 .withClientCertData(config.clientCertData())
375 .withClientKeyData(config.clientKeyData());
376 }
377
378 return new DefaultKubernetesClient(configBuilder.build());
379 }
Jian Li3d1111e2019-02-22 02:02:13 +0900380
381 /**
382 * Obtains workable kubernetes client.
383 *
384 * @param service kubernetes API service
385 * @return kubernetes client
386 */
387 public static KubernetesClient k8sClient(K8sApiConfigService service) {
388 K8sApiConfig config =
389 service.apiConfigs().stream().findAny().orElse(null);
390 if (config == null) {
391 log.error("Failed to find valid kubernetes API configuration.");
392 return null;
393 }
394
395 KubernetesClient client = k8sClient(config);
396
397 if (client == null) {
398 log.error("Failed to connect to kubernetes API server.");
399 return null;
400 }
401
402 return client;
403 }
Jian Li2cc2b632019-02-18 00:56:40 +0900404
405 /**
406 * Obtains the kubernetes node IP and kubernetes network gateway IP map.
407 *
408 * @param nodeService kubernetes node service
409 * @param networkService kubernetes network service
410 * @return kubernetes node IP and kubernetes network gateway IP map
411 */
412 public static Map<String, String> nodeIpGatewayIpMap(K8sNodeService nodeService,
413 K8sNetworkService networkService) {
414 Map<String, String> ipMap = Maps.newConcurrentMap();
415
416 nodeService.completeNodes().forEach(n -> {
417 K8sNetwork network = networkService.network(n.hostname());
418 if (network != null) {
Jian Li4294af72020-10-07 02:12:33 +0900419 ipMap.put(n.nodeIp().toString(), network.gatewayIp().toString());
Jian Li2cc2b632019-02-18 00:56:40 +0900420 }
421 });
422
423 return ipMap;
424 }
Jian Li004526d2019-02-25 16:26:27 +0900425
426 /**
Jian Li73d3b6a2019-07-08 18:07:53 +0900427 * Returns a shifted IP address.
428 *
429 * @param ipAddress IP address to be shifted
430 * @param shiftPrefix A IP prefix used in shifted IP address
431 * @return shifted Ip address
432 */
433 public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
434 String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
435 return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
436 }
437
438 /**
Jian Li140d8a22019-04-24 23:41:44 +0900439 * Returns an unshifted IP address.
Jian Li004526d2019-02-25 16:26:27 +0900440 *
Jian Li140d8a22019-04-24 23:41:44 +0900441 * @param ipAddress IP address to be unshifted
442 * @param ipPrefix IP prefix which to be used for unshifting
443 * @param cidr a POD network CIDR
444 * @return unshifted IP address
Jian Li004526d2019-02-25 16:26:27 +0900445 */
Jian Li140d8a22019-04-24 23:41:44 +0900446 public static String unshiftIpDomain(String ipAddress,
447 String ipPrefix,
448 String cidr) {
Jian Li004526d2019-02-25 16:26:27 +0900449
Jian Li140d8a22019-04-24 23:41:44 +0900450 String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
451 return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
452 }
Jian Li004526d2019-02-25 16:26:27 +0900453
Jian Li140d8a22019-04-24 23:41:44 +0900454 /**
455 * Returns the B class IP prefix of the given CIDR.
456 *
457 * @param cidr CIDR
458 * @return IP prefix
459 */
460 public static String getBclassIpPrefixFromCidr(String cidr) {
461 if (cidr == null) {
462 return null;
463 }
464 return cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
465 }
Jian Li004526d2019-02-25 16:26:27 +0900466
Jian Li140d8a22019-04-24 23:41:44 +0900467 /**
468 * Returns the A class IP prefix of the given CIDR.
469 *
470 * @param cidr CIDR
471 * @return IP prefix
472 */
473 public static String getAclassIpPrefixFromCidr(String cidr) {
474 if (cidr == null) {
475 return null;
476 }
477 return cidr.split("\\.")[0];
478 }
479
480 /**
481 * Returns the map of port range.
482 *
483 * @param portMin minimum port number
484 * @param portMax maximum port number
485 * @return map of port range
486 */
487 public static Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
488
489 boolean processing = true;
490 int start = portMin;
491 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
492 while (processing) {
493 String minStr = Integer.toBinaryString(start);
494 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
495
496 int mask = testMasks(binStrMinPadded, start, portMax);
497 int maskStart = binLower(binStrMinPadded, mask);
498 int maskEnd = binHigher(binStrMinPadded, mask);
499
500 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
501 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
502 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
503
504 start = maskEnd + 1;
505 if (start > portMax) {
506 processing = false;
507 }
508 }
509
510 return portMaskMap;
511 }
512
Jian Lie1a5b8f2019-07-23 17:13:19 +0900513 /**
514 * Returns the namespace hash value by given POD IP.
515 *
516 * @param k8sPodService kubernetes POD service
517 * @param k8sNamespaceService kubernetes namespace service
518 * @param podIp POD IP address
519 * @return namespace hash value
520 */
521 public static Integer namespaceHashByPodIp(K8sPodService k8sPodService,
522 K8sNamespaceService k8sNamespaceService,
523 String podIp) {
524 String ns = k8sPodService.pods().stream()
525 .filter(pod -> pod.getStatus().getPodIP() != null)
526 .filter(pod -> pod.getStatus().getPodIP().equals(podIp))
527 .map(pod -> pod.getMetadata().getNamespace())
528 .findAny().orElse(null);
529
530 if (ns != null) {
531 return k8sNamespaceService.namespaces().stream()
532 .filter(n -> n.getMetadata().getName().equals(ns))
533 .map(Namespace::hashCode).findAny().orElse(null);
534 } else {
535 return null;
536 }
537 }
538
539 /**
540 * Returns the namespace hash value by given service IP.
541 *
542 * @param k8sServiceService kubernetes service service
543 * @param k8sNamespaceService kubernetes namespace service
544 * @param serviceIp service IP address
545 * @return namespace hash value
546 */
547 public static int namespaceHashByServiceIp(K8sServiceService k8sServiceService,
548 K8sNamespaceService k8sNamespaceService,
549 String serviceIp) {
550 String ns = k8sServiceService.services().stream()
551 .filter(service -> service.getSpec().getClusterIP() != null)
552 .filter(service -> service.getSpec().getClusterIP().equalsIgnoreCase(serviceIp))
553 .map(service -> service.getMetadata().getNamespace())
554 .findAny().orElse(null);
555
556 if (ns != null) {
557 return namespaceHashByNamespace(k8sNamespaceService, ns);
558 } else {
559 return DEFAULT_NAMESPACE_HASH;
560 }
561 }
562
563 /**
564 * Returns the namespace hash value by given namespace name.
565 *
566 * @param k8sNamespaceService kubernetes namespace service
567 * @param ns namespace name
568 * @return namespace hash value
569 */
570 public static int namespaceHashByNamespace(K8sNamespaceService k8sNamespaceService,
571 String ns) {
572
573 return k8sNamespaceService.namespaces().stream()
574 .filter(n -> n.getMetadata().getName() != null)
575 .filter(n -> n.getMetadata().getName().equalsIgnoreCase(ns))
576 .map(Namespace::hashCode).findAny().orElse(DEFAULT_NAMESPACE_HASH);
577 }
578
579 /**
580 * Returns POD instance by POD IP address.
581 *
582 * @param podService kubernetes POD service
583 * @param podIp POD IP address
584 * @return POD instance
585 */
586 public static Pod podByIp(K8sPodService podService, String podIp) {
587 return podService.pods().stream()
588 .filter(pod -> pod.getStatus().getPodIP() != null)
589 .filter(pod -> pod.getStatus().getPodIP().equals(podIp))
590 .findAny().orElse(null);
591 }
592
593 /**
594 * Returns the container port number by given container port name.
595 *
596 * @param pod kubernetes POD
597 * @param portName port name
598 * @return container port number,
599 * return 0 if there is no port number mapped with the given port name
600 */
601 public static int portNumberByName(Pod pod, String portName) {
Jian Li5cf3b002019-08-30 17:57:53 +0900602
603 if (pod == null || pod.getSpec() == null) {
604 return 0;
605 }
606
Jian Lie1a5b8f2019-07-23 17:13:19 +0900607 for (Container container : pod.getSpec().getContainers()) {
608 for (ContainerPort cp : container.getPorts()) {
609 if (cp.getName() != null && cp.getName().equals(portName)) {
610 return cp.getContainerPort();
611 }
612 }
613 }
614
615 return 0;
616 }
617
Jian Li4bd6f2b2019-08-16 21:39:27 +0900618 /**
619 * Synchronizes port from kubernetes POD.
620 *
621 * @param pod kubernetes POD
622 * @param adminService admin service
623 */
624 public static void syncPortFromPod(Pod pod, K8sNetworkAdminService adminService) {
625 Map<String, String> annotations = pod.getMetadata().getAnnotations();
626 if (annotations != null && !annotations.isEmpty() &&
627 annotations.get(PORT_ID) != null) {
628 String portId = annotations.get(PORT_ID);
629
630 K8sPort oldPort = adminService.port(portId);
631
632 String networkId = annotations.get(NETWORK_ID);
633 DeviceId deviceId = DeviceId.deviceId(annotations.get(DEVICE_ID));
634 PortNumber portNumber = PortNumber.portNumber(annotations.get(PORT_NUMBER));
635 IpAddress ipAddress = IpAddress.valueOf(annotations.get(IP_ADDRESS));
636 MacAddress macAddress = MacAddress.valueOf(annotations.get(MAC_ADDRESS));
637
638 K8sPort newPort = DefaultK8sPort.builder()
639 .portId(portId)
640 .networkId(networkId)
641 .deviceId(deviceId)
642 .ipAddress(ipAddress)
643 .macAddress(macAddress)
644 .portNumber(portNumber)
645 .state(INACTIVE)
646 .build();
647
648 if (oldPort == null) {
649 adminService.createPort(newPort);
650 } else {
651 adminService.updatePort(newPort);
652 }
653 }
654 }
655
Jian Lia6f58382019-12-16 14:22:13 +0900656 /**
657 * Generates string format based on the given string length list.
658 *
659 * @param stringLengths a list of string lengths
660 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
661 */
662 public static String genFormatString(List<Integer> stringLengths) {
663 StringBuilder fsb = new StringBuilder();
664 stringLengths.forEach(length -> {
665 fsb.append("%-");
666 fsb.append(length);
667 fsb.append("s");
668 });
669 return fsb.toString();
670 }
671
Jian Lieab51352020-09-11 03:29:16 +0900672 /**
673 * Returns all device identifiers belong to kubernetes nodes and hosts.
674 *
675 * @param nodeService node service
676 * @param hostService host service
677 * @return all device identifiers belong to kubernetes nodes and hosts
678 */
679 public static Set<DeviceId> allK8sDevices(K8sNodeService nodeService,
680 K8sHostService hostService) {
681 Set<DeviceId> allDevIds = new HashSet<>();
682
683 Set<DeviceId> intgDevIds = nodeService.completeNodes().stream()
684 .map(K8sNode::intgBridge).collect(Collectors.toSet());
685 Set<DeviceId> extDevIds = nodeService.completeNodes().stream()
686 .map(K8sNode::extBridge).collect(Collectors.toSet());
687 Set<DeviceId> tunDevIds = nodeService.completeNodes().stream()
688 .map(K8sNode::tunBridge).collect(Collectors.toSet());
689 Set<DeviceId> localDevIds = nodeService.completeNodes().stream()
690 .map(K8sNode::localBridge).collect(Collectors.toSet());
691
692 Set<DeviceId> hostTunDevIds = new HashSet<>();
693 Set<DeviceId> hostRouterDevIds = new HashSet<>();
694
695 for (K8sHost host : hostService.completeHosts()) {
696 Set<K8sTunnelBridge> hostTunBrs = host.tunBridges();
697 Set<K8sRouterBridge> hostRouterBrs = host.routerBridges();
698 hostTunDevIds.addAll(hostTunBrs.stream().map(K8sTunnelBridge::deviceId)
699 .collect(Collectors.toSet()));
700 hostRouterDevIds.addAll(hostRouterBrs.stream().map(K8sRouterBridge::deviceId)
701 .collect(Collectors.toSet()));
702 }
703
704 allDevIds.addAll(intgDevIds);
705 allDevIds.addAll(extDevIds);
706 allDevIds.addAll(tunDevIds);
707 allDevIds.addAll(localDevIds);
708 allDevIds.addAll(hostTunDevIds);
709 allDevIds.addAll(hostRouterDevIds);
710
711 return allDevIds;
712 }
713
Jian Li140d8a22019-04-24 23:41:44 +0900714 private static int binLower(String binStr, int bits) {
715 StringBuilder outBin = new StringBuilder(
716 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
717 for (int i = 0; i < bits; i++) {
718 outBin.append(STR_ZERO);
719 }
720
721 return Integer.parseInt(outBin.toString(), MASK_RADIX);
722 }
723
724 private static int binHigher(String binStr, int bits) {
725 StringBuilder outBin = new StringBuilder(
726 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
727 for (int i = 0; i < bits; i++) {
728 outBin.append(STR_ONE);
729 }
730
731 return Integer.parseInt(outBin.toString(), MASK_RADIX);
732 }
733
734 private static int testMasks(String binStr, int start, int end) {
735 int mask = MASK_BEGIN_IDX;
736 for (; mask <= MASK_MAX_IDX; mask++) {
737 int maskStart = binLower(binStr, mask);
738 int maskEnd = binHigher(binStr, mask);
739 if (maskStart < start || maskEnd > end) {
740 return mask - 1;
741 }
742 }
743
744 return mask;
745 }
746
747 private static String getMask(int bits) {
748 switch (bits) {
749 case 0: return "ffff";
750 case 1: return "fffe";
751 case 2: return "fffc";
752 case 3: return "fff8";
753 case 4: return "fff0";
754 case 5: return "ffe0";
755 case 6: return "ffc0";
756 case 7: return "ff80";
757 case 8: return "ff00";
758 case 9: return "fe00";
759 case 10: return "fc00";
760 case 11: return "f800";
761 case 12: return "f000";
762 case 13: return "e000";
763 case 14: return "c000";
764 case 15: return "8000";
765 case 16: return "0000";
766 default: return null;
767 }
Jian Li004526d2019-02-25 16:26:27 +0900768 }
Jian Libde20bf2019-01-25 17:34:43 +0900769}