blob: 0b8c4c96799a35ff06c461fd6be0378f65b13e5a [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 */
127 public static boolean existingContainerPort(String sourceName, String comparedName) {
128 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 /**
144 * Returns the tunnel port number with specified net ID and kubernetes node.
145 *
146 * @param netId network ID
147 * @param netService network service
148 * @param node kubernetes node
149 * @return tunnel port number
150 */
151 public static PortNumber tunnelPortNumByNetId(String netId,
152 K8sNetworkService netService,
153 K8sNode node) {
154 K8sNetwork.Type netType = netService.network(netId).type();
155
156 if (netType == null) {
157 return null;
158 }
159
160 return tunnelPortNumByNetType(netType, node);
161 }
162
163 /**
164 * Returns the tunnel port number with specified net type and kubernetes node.
165 *
166 * @param netType network type
167 * @param node kubernetes node
168 * @return tunnel port number
169 */
170 public static PortNumber tunnelPortNumByNetType(K8sNetwork.Type netType,
171 K8sNode node) {
Jian Li732c3422020-09-07 17:01:11 +0900172 if (node.mode() == PASSTHROUGH) {
173 K8sHostService hostService =
174 DefaultServiceDirectory.getService(K8sHostService.class);
175 Port port = null;
176 for (K8sHost host : hostService.hosts()) {
177 if (host.nodeNames().contains(node.hostname())) {
178 for (K8sTunnelBridge bridge : host.tunBridges()) {
179 if (bridge.tunnelId() == node.segmentId()) {
180 String portName = netType.name().toLowerCase() +
181 "-" + node.segmentId();
182 port = port(bridge.deviceId(), portName);
183 }
184 }
185 }
186 }
187
188 if (port == null) {
Jian Li4aa17642019-01-30 00:01:11 +0900189 return null;
Jian Li732c3422020-09-07 17:01:11 +0900190 } else {
191 return port.number();
192 }
193
194 } else {
195 switch (netType) {
196 case VXLAN:
197 return node.vxlanPortNum();
198 case GRE:
199 return node.grePortNum();
200 case GENEVE:
201 return node.genevePortNum();
202 default:
203 return null;
204 }
Jian Li4aa17642019-01-30 00:01:11 +0900205 }
206 }
207
208 /**
Jian Li732c3422020-09-07 17:01:11 +0900209 * Obtains the port from the device with the given port name.
210 *
211 * @param deviceId device identifier
212 * @param portName port name
213 * @return port object
214 */
215 public static Port port(DeviceId deviceId, String portName) {
216 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
217 return deviceService.getPorts(deviceId).stream()
218 .filter(p -> p.isEnabled() &&
219 Objects.equals(p.annotations().value(PORT_NAME), portName))
220 .findAny().orElse(null);
221 }
222
223 /**
Jian Li4aa17642019-01-30 00:01:11 +0900224 * Obtains the property value with specified property key name.
225 *
226 * @param properties a collection of properties
227 * @param name key name
228 * @return mapping value
229 */
230 public static String getPropertyValue(Set<ConfigProperty> properties,
231 String name) {
232 Optional<ConfigProperty> property =
233 properties.stream().filter(p -> p.name().equals(name)).findFirst();
234 return property.map(ConfigProperty::value).orElse(null);
Jian Libde20bf2019-01-25 17:34:43 +0900235 }
236
237 /**
238 * Prints out the JSON string in pretty format.
239 *
240 * @param mapper Object mapper
241 * @param jsonString JSON string
242 * @return pretty formatted JSON string
243 */
244 public static String prettyJson(ObjectMapper mapper, String jsonString) {
245 try {
246 Object jsonObject = mapper.readValue(jsonString, Object.class);
247 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
248 } catch (JsonParseException e) {
Jian Li003d5692019-08-26 23:19:38 +0900249 log.debug("JsonParseException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900250 } catch (JsonMappingException e) {
Jian Li003d5692019-08-26 23:19:38 +0900251 log.debug("JsonMappingException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900252 } catch (JsonProcessingException e) {
Jian Li003d5692019-08-26 23:19:38 +0900253 log.debug("JsonProcessingException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900254 } catch (IOException e) {
Jian Li003d5692019-08-26 23:19:38 +0900255 log.debug("IOException", e);
Jian Libde20bf2019-01-25 17:34:43 +0900256 }
257 return null;
258 }
Jian Li9b199162019-02-10 18:00:35 +0900259
260 /**
261 * Obtains valid IP addresses of the given subnet.
262 *
263 * @param cidr CIDR
264 * @return set of IP addresses
265 */
266 public static Set<IpAddress> getSubnetIps(String cidr) {
267 SubnetUtils utils = new SubnetUtils(cidr);
Jian Li7970b712019-05-03 20:58:21 +0900268 utils.setInclusiveHostCount(false);
Jian Li9b199162019-02-10 18:00:35 +0900269 SubnetUtils.SubnetInfo info = utils.getInfo();
270 Set<String> allAddresses =
271 new HashSet<>(Arrays.asList(info.getAllAddresses()));
272
273 if (allAddresses.size() > 2) {
Jian Li7970b712019-05-03 20:58:21 +0900274 allAddresses.remove(info.getLowAddress());
275 allAddresses.remove(info.getHighAddress());
Jian Li9b199162019-02-10 18:00:35 +0900276 }
277
278 return allAddresses.stream()
279 .map(IpAddress::valueOf).collect(Collectors.toSet());
280 }
Jian Li85387732019-02-19 23:56:18 +0900281
282 /**
Jian Li2cc2b632019-02-18 00:56:40 +0900283 * Obtains flow group key from the given id.
284 *
285 * @param groupId flow group identifier
286 * @return flow group key
287 */
288 public static GroupKey getGroupKey(int groupId) {
289 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
290 }
291
292 /**
Jian Li85387732019-02-19 23:56:18 +0900293 * Generates endpoint URL by referring to scheme, ipAddress and port.
294 *
295 * @param scheme scheme
296 * @param ipAddress IP address
297 * @param port port number
298 * @return generated endpoint URL
299 */
300 public static String endpoint(K8sApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
301 StringBuilder endpoint = new StringBuilder();
302 String protocol = StringUtils.lowerCase(scheme.name());
303
304 endpoint.append(protocol);
305 endpoint.append(COLON_SLASH);
306 endpoint.append(ipAddress.toString());
307 endpoint.append(COLON);
308 endpoint.append(port);
309
310 return endpoint.toString();
311 }
312
313 /**
314 * Generates endpoint URL by referring to scheme, ipAddress and port.
315 *
316 * @param apiConfig kubernetes API config
317 * @return generated endpoint URL
318 */
319 public static String endpoint(K8sApiConfig apiConfig) {
320 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
321 }
322
323 /**
324 * Obtains workable kubernetes client.
325 *
326 * @param config kubernetes API config
327 * @return kubernetes client
328 */
329 public static KubernetesClient k8sClient(K8sApiConfig config) {
330 if (config == null) {
331 log.warn("Kubernetes API server config is empty.");
332 return null;
333 }
334
335 String endpoint = endpoint(config);
336
337 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
338
339 if (config.scheme() == K8sApiConfig.Scheme.HTTPS) {
340 configBuilder.withTrustCerts(true)
341 .withOauthToken(config.token())
342 .withCaCertData(config.caCertData())
343 .withClientCertData(config.clientCertData())
344 .withClientKeyData(config.clientKeyData());
345 }
346
347 return new DefaultKubernetesClient(configBuilder.build());
348 }
Jian Li3d1111e2019-02-22 02:02:13 +0900349
350 /**
351 * Obtains workable kubernetes client.
352 *
353 * @param service kubernetes API service
354 * @return kubernetes client
355 */
356 public static KubernetesClient k8sClient(K8sApiConfigService service) {
357 K8sApiConfig config =
358 service.apiConfigs().stream().findAny().orElse(null);
359 if (config == null) {
360 log.error("Failed to find valid kubernetes API configuration.");
361 return null;
362 }
363
364 KubernetesClient client = k8sClient(config);
365
366 if (client == null) {
367 log.error("Failed to connect to kubernetes API server.");
368 return null;
369 }
370
371 return client;
372 }
Jian Li2cc2b632019-02-18 00:56:40 +0900373
374 /**
375 * Obtains the kubernetes node IP and kubernetes network gateway IP map.
376 *
377 * @param nodeService kubernetes node service
378 * @param networkService kubernetes network service
379 * @return kubernetes node IP and kubernetes network gateway IP map
380 */
381 public static Map<String, String> nodeIpGatewayIpMap(K8sNodeService nodeService,
382 K8sNetworkService networkService) {
383 Map<String, String> ipMap = Maps.newConcurrentMap();
384
385 nodeService.completeNodes().forEach(n -> {
386 K8sNetwork network = networkService.network(n.hostname());
387 if (network != null) {
388 ipMap.put(n.dataIp().toString(), network.gatewayIp().toString());
389 }
390 });
391
392 return ipMap;
393 }
Jian Li004526d2019-02-25 16:26:27 +0900394
395 /**
Jian Li73d3b6a2019-07-08 18:07:53 +0900396 * Returns a shifted IP address.
397 *
398 * @param ipAddress IP address to be shifted
399 * @param shiftPrefix A IP prefix used in shifted IP address
400 * @return shifted Ip address
401 */
402 public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
403 String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
404 return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
405 }
406
407 /**
Jian Li140d8a22019-04-24 23:41:44 +0900408 * Returns an unshifted IP address.
Jian Li004526d2019-02-25 16:26:27 +0900409 *
Jian Li140d8a22019-04-24 23:41:44 +0900410 * @param ipAddress IP address to be unshifted
411 * @param ipPrefix IP prefix which to be used for unshifting
412 * @param cidr a POD network CIDR
413 * @return unshifted IP address
Jian Li004526d2019-02-25 16:26:27 +0900414 */
Jian Li140d8a22019-04-24 23:41:44 +0900415 public static String unshiftIpDomain(String ipAddress,
416 String ipPrefix,
417 String cidr) {
Jian Li004526d2019-02-25 16:26:27 +0900418
Jian Li140d8a22019-04-24 23:41:44 +0900419 String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
420 return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
421 }
Jian Li004526d2019-02-25 16:26:27 +0900422
Jian Li140d8a22019-04-24 23:41:44 +0900423 /**
424 * Returns the B class IP prefix of the given CIDR.
425 *
426 * @param cidr CIDR
427 * @return IP prefix
428 */
429 public static String getBclassIpPrefixFromCidr(String cidr) {
430 if (cidr == null) {
431 return null;
432 }
433 return cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
434 }
Jian Li004526d2019-02-25 16:26:27 +0900435
Jian Li140d8a22019-04-24 23:41:44 +0900436 /**
437 * Returns the A class IP prefix of the given CIDR.
438 *
439 * @param cidr CIDR
440 * @return IP prefix
441 */
442 public static String getAclassIpPrefixFromCidr(String cidr) {
443 if (cidr == null) {
444 return null;
445 }
446 return cidr.split("\\.")[0];
447 }
448
449 /**
450 * Returns the map of port range.
451 *
452 * @param portMin minimum port number
453 * @param portMax maximum port number
454 * @return map of port range
455 */
456 public static Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
457
458 boolean processing = true;
459 int start = portMin;
460 Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
461 while (processing) {
462 String minStr = Integer.toBinaryString(start);
463 String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
464
465 int mask = testMasks(binStrMinPadded, start, portMax);
466 int maskStart = binLower(binStrMinPadded, mask);
467 int maskEnd = binHigher(binStrMinPadded, mask);
468
469 log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
470 portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
471 Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
472
473 start = maskEnd + 1;
474 if (start > portMax) {
475 processing = false;
476 }
477 }
478
479 return portMaskMap;
480 }
481
Jian Lie1a5b8f2019-07-23 17:13:19 +0900482 /**
483 * Returns the namespace hash value by given POD IP.
484 *
485 * @param k8sPodService kubernetes POD service
486 * @param k8sNamespaceService kubernetes namespace service
487 * @param podIp POD IP address
488 * @return namespace hash value
489 */
490 public static Integer namespaceHashByPodIp(K8sPodService k8sPodService,
491 K8sNamespaceService k8sNamespaceService,
492 String podIp) {
493 String ns = k8sPodService.pods().stream()
494 .filter(pod -> pod.getStatus().getPodIP() != null)
495 .filter(pod -> pod.getStatus().getPodIP().equals(podIp))
496 .map(pod -> pod.getMetadata().getNamespace())
497 .findAny().orElse(null);
498
499 if (ns != null) {
500 return k8sNamespaceService.namespaces().stream()
501 .filter(n -> n.getMetadata().getName().equals(ns))
502 .map(Namespace::hashCode).findAny().orElse(null);
503 } else {
504 return null;
505 }
506 }
507
508 /**
509 * Returns the namespace hash value by given service IP.
510 *
511 * @param k8sServiceService kubernetes service service
512 * @param k8sNamespaceService kubernetes namespace service
513 * @param serviceIp service IP address
514 * @return namespace hash value
515 */
516 public static int namespaceHashByServiceIp(K8sServiceService k8sServiceService,
517 K8sNamespaceService k8sNamespaceService,
518 String serviceIp) {
519 String ns = k8sServiceService.services().stream()
520 .filter(service -> service.getSpec().getClusterIP() != null)
521 .filter(service -> service.getSpec().getClusterIP().equalsIgnoreCase(serviceIp))
522 .map(service -> service.getMetadata().getNamespace())
523 .findAny().orElse(null);
524
525 if (ns != null) {
526 return namespaceHashByNamespace(k8sNamespaceService, ns);
527 } else {
528 return DEFAULT_NAMESPACE_HASH;
529 }
530 }
531
532 /**
533 * Returns the namespace hash value by given namespace name.
534 *
535 * @param k8sNamespaceService kubernetes namespace service
536 * @param ns namespace name
537 * @return namespace hash value
538 */
539 public static int namespaceHashByNamespace(K8sNamespaceService k8sNamespaceService,
540 String ns) {
541
542 return k8sNamespaceService.namespaces().stream()
543 .filter(n -> n.getMetadata().getName() != null)
544 .filter(n -> n.getMetadata().getName().equalsIgnoreCase(ns))
545 .map(Namespace::hashCode).findAny().orElse(DEFAULT_NAMESPACE_HASH);
546 }
547
548 /**
549 * Returns POD instance by POD IP address.
550 *
551 * @param podService kubernetes POD service
552 * @param podIp POD IP address
553 * @return POD instance
554 */
555 public static Pod podByIp(K8sPodService podService, String podIp) {
556 return podService.pods().stream()
557 .filter(pod -> pod.getStatus().getPodIP() != null)
558 .filter(pod -> pod.getStatus().getPodIP().equals(podIp))
559 .findAny().orElse(null);
560 }
561
562 /**
563 * Returns the container port number by given container port name.
564 *
565 * @param pod kubernetes POD
566 * @param portName port name
567 * @return container port number,
568 * return 0 if there is no port number mapped with the given port name
569 */
570 public static int portNumberByName(Pod pod, String portName) {
Jian Li5cf3b002019-08-30 17:57:53 +0900571
572 if (pod == null || pod.getSpec() == null) {
573 return 0;
574 }
575
Jian Lie1a5b8f2019-07-23 17:13:19 +0900576 for (Container container : pod.getSpec().getContainers()) {
577 for (ContainerPort cp : container.getPorts()) {
578 if (cp.getName() != null && cp.getName().equals(portName)) {
579 return cp.getContainerPort();
580 }
581 }
582 }
583
584 return 0;
585 }
586
Jian Li4bd6f2b2019-08-16 21:39:27 +0900587 /**
588 * Synchronizes port from kubernetes POD.
589 *
590 * @param pod kubernetes POD
591 * @param adminService admin service
592 */
593 public static void syncPortFromPod(Pod pod, K8sNetworkAdminService adminService) {
594 Map<String, String> annotations = pod.getMetadata().getAnnotations();
595 if (annotations != null && !annotations.isEmpty() &&
596 annotations.get(PORT_ID) != null) {
597 String portId = annotations.get(PORT_ID);
598
599 K8sPort oldPort = adminService.port(portId);
600
601 String networkId = annotations.get(NETWORK_ID);
602 DeviceId deviceId = DeviceId.deviceId(annotations.get(DEVICE_ID));
603 PortNumber portNumber = PortNumber.portNumber(annotations.get(PORT_NUMBER));
604 IpAddress ipAddress = IpAddress.valueOf(annotations.get(IP_ADDRESS));
605 MacAddress macAddress = MacAddress.valueOf(annotations.get(MAC_ADDRESS));
606
607 K8sPort newPort = DefaultK8sPort.builder()
608 .portId(portId)
609 .networkId(networkId)
610 .deviceId(deviceId)
611 .ipAddress(ipAddress)
612 .macAddress(macAddress)
613 .portNumber(portNumber)
614 .state(INACTIVE)
615 .build();
616
617 if (oldPort == null) {
618 adminService.createPort(newPort);
619 } else {
620 adminService.updatePort(newPort);
621 }
622 }
623 }
624
Jian Lia6f58382019-12-16 14:22:13 +0900625 /**
626 * Generates string format based on the given string length list.
627 *
628 * @param stringLengths a list of string lengths
629 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
630 */
631 public static String genFormatString(List<Integer> stringLengths) {
632 StringBuilder fsb = new StringBuilder();
633 stringLengths.forEach(length -> {
634 fsb.append("%-");
635 fsb.append(length);
636 fsb.append("s");
637 });
638 return fsb.toString();
639 }
640
Jian Lieab51352020-09-11 03:29:16 +0900641 /**
642 * Returns all device identifiers belong to kubernetes nodes and hosts.
643 *
644 * @param nodeService node service
645 * @param hostService host service
646 * @return all device identifiers belong to kubernetes nodes and hosts
647 */
648 public static Set<DeviceId> allK8sDevices(K8sNodeService nodeService,
649 K8sHostService hostService) {
650 Set<DeviceId> allDevIds = new HashSet<>();
651
652 Set<DeviceId> intgDevIds = nodeService.completeNodes().stream()
653 .map(K8sNode::intgBridge).collect(Collectors.toSet());
654 Set<DeviceId> extDevIds = nodeService.completeNodes().stream()
655 .map(K8sNode::extBridge).collect(Collectors.toSet());
656 Set<DeviceId> tunDevIds = nodeService.completeNodes().stream()
657 .map(K8sNode::tunBridge).collect(Collectors.toSet());
658 Set<DeviceId> localDevIds = nodeService.completeNodes().stream()
659 .map(K8sNode::localBridge).collect(Collectors.toSet());
660
661 Set<DeviceId> hostTunDevIds = new HashSet<>();
662 Set<DeviceId> hostRouterDevIds = new HashSet<>();
663
664 for (K8sHost host : hostService.completeHosts()) {
665 Set<K8sTunnelBridge> hostTunBrs = host.tunBridges();
666 Set<K8sRouterBridge> hostRouterBrs = host.routerBridges();
667 hostTunDevIds.addAll(hostTunBrs.stream().map(K8sTunnelBridge::deviceId)
668 .collect(Collectors.toSet()));
669 hostRouterDevIds.addAll(hostRouterBrs.stream().map(K8sRouterBridge::deviceId)
670 .collect(Collectors.toSet()));
671 }
672
673 allDevIds.addAll(intgDevIds);
674 allDevIds.addAll(extDevIds);
675 allDevIds.addAll(tunDevIds);
676 allDevIds.addAll(localDevIds);
677 allDevIds.addAll(hostTunDevIds);
678 allDevIds.addAll(hostRouterDevIds);
679
680 return allDevIds;
681 }
682
Jian Li140d8a22019-04-24 23:41:44 +0900683 private static int binLower(String binStr, int bits) {
684 StringBuilder outBin = new StringBuilder(
685 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
686 for (int i = 0; i < bits; i++) {
687 outBin.append(STR_ZERO);
688 }
689
690 return Integer.parseInt(outBin.toString(), MASK_RADIX);
691 }
692
693 private static int binHigher(String binStr, int bits) {
694 StringBuilder outBin = new StringBuilder(
695 binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
696 for (int i = 0; i < bits; i++) {
697 outBin.append(STR_ONE);
698 }
699
700 return Integer.parseInt(outBin.toString(), MASK_RADIX);
701 }
702
703 private static int testMasks(String binStr, int start, int end) {
704 int mask = MASK_BEGIN_IDX;
705 for (; mask <= MASK_MAX_IDX; mask++) {
706 int maskStart = binLower(binStr, mask);
707 int maskEnd = binHigher(binStr, mask);
708 if (maskStart < start || maskEnd > end) {
709 return mask - 1;
710 }
711 }
712
713 return mask;
714 }
715
716 private static String getMask(int bits) {
717 switch (bits) {
718 case 0: return "ffff";
719 case 1: return "fffe";
720 case 2: return "fffc";
721 case 3: return "fff8";
722 case 4: return "fff0";
723 case 5: return "ffe0";
724 case 6: return "ffc0";
725 case 7: return "ff80";
726 case 8: return "ff00";
727 case 9: return "fe00";
728 case 10: return "fc00";
729 case 11: return "f800";
730 case 12: return "f000";
731 case 13: return "e000";
732 case 14: return "c000";
733 case 15: return "8000";
734 case 16: return "0000";
735 default: return null;
736 }
Jian Li004526d2019-02-25 16:26:27 +0900737 }
Jian Libde20bf2019-01-25 17:34:43 +0900738}