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