blob: b49bbccd8e981695190ca3860783c00d12cc2461 [file] [log] [blame]
Jian Li43244382021-01-09 00:19:02 +09001/*
2 * Copyright 2021-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.kubevirtnetworking.util;
17
Jian Li0c656f02021-06-07 13:32:39 +090018import com.fasterxml.jackson.databind.JsonNode;
Jian Lif97a07e2021-01-13 18:05:00 +090019import com.fasterxml.jackson.databind.ObjectMapper;
Jian Li0c656f02021-06-07 13:32:39 +090020import com.fasterxml.jackson.databind.node.ArrayNode;
Jian Li556709c2021-02-03 17:54:28 +090021import com.google.common.base.Strings;
Jian Lid4296d02021-03-12 18:03:58 +090022import com.google.common.collect.ImmutableSet;
Jian Li034820d2021-01-15 16:58:48 +090023import io.fabric8.kubernetes.client.ConfigBuilder;
24import io.fabric8.kubernetes.client.DefaultKubernetesClient;
25import io.fabric8.kubernetes.client.KubernetesClient;
Jian Li43244382021-01-09 00:19:02 +090026import org.apache.commons.lang.StringUtils;
Jian Li3ba5c582021-01-14 11:30:36 +090027import org.apache.commons.net.util.SubnetUtils;
Jian Lica20b712021-01-18 00:19:31 +090028import org.json.JSONException;
29import org.json.JSONObject;
Jian Li858ccd72021-02-04 17:25:01 +090030import org.onlab.osgi.DefaultServiceDirectory;
Daniel Park157947f2021-04-09 17:50:53 +090031import org.onlab.packet.ARP;
32import org.onlab.packet.Ethernet;
Daniel Park2884b232021-03-04 18:58:47 +090033import org.onlab.packet.Ip4Address;
Jian Li3ba5c582021-01-14 11:30:36 +090034import org.onlab.packet.IpAddress;
Jian Lica20b712021-01-18 00:19:31 +090035import org.onlab.packet.MacAddress;
Jian Li43244382021-01-09 00:19:02 +090036import org.onosproject.cfg.ConfigProperty;
Jian Lica20b712021-01-18 00:19:31 +090037import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
Daniel Park05a94582021-05-12 10:57:02 +090038import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
39import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerService;
Jian Lica20b712021-01-18 00:19:31 +090040import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
Daniel Parkf3136042021-03-10 07:49:11 +090041import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
Jian Lica20b712021-01-18 00:19:31 +090042import org.onosproject.kubevirtnetworking.api.KubevirtPort;
Daniel Park2884b232021-03-04 18:58:47 +090043import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
44import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
Jian Li034820d2021-01-15 16:58:48 +090045import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
46import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Li858ccd72021-02-04 17:25:01 +090047import org.onosproject.kubevirtnode.api.KubevirtNode;
Daniel Park2884b232021-03-04 18:58:47 +090048import org.onosproject.kubevirtnode.api.KubevirtNodeService;
Jian Li858ccd72021-02-04 17:25:01 +090049import org.onosproject.net.DeviceId;
50import org.onosproject.net.Port;
51import org.onosproject.net.PortNumber;
52import org.onosproject.net.device.DeviceService;
Daniel Park05a94582021-05-12 10:57:02 +090053import org.onosproject.net.group.DefaultGroupKey;
54import org.onosproject.net.group.GroupKey;
Jian Li43244382021-01-09 00:19:02 +090055import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
Jian Li94b6d162021-04-15 17:09:11 +090057import org.xbill.DNS.Address;
Jian Li43244382021-01-09 00:19:02 +090058
Jian Lif97a07e2021-01-13 18:05:00 +090059import java.io.IOException;
Jian Li94b6d162021-04-15 17:09:11 +090060import java.net.InetAddress;
61import java.net.UnknownHostException;
Jian Li3ba5c582021-01-14 11:30:36 +090062import java.util.Arrays;
63import java.util.HashSet;
Jian Lif97a07e2021-01-13 18:05:00 +090064import java.util.List;
Jian Li858ccd72021-02-04 17:25:01 +090065import java.util.Objects;
Jian Li43244382021-01-09 00:19:02 +090066import java.util.Optional;
67import java.util.Set;
Jian Li3ba5c582021-01-14 11:30:36 +090068import java.util.stream.Collectors;
Jian Li43244382021-01-09 00:19:02 +090069
Jian Li858ccd72021-02-04 17:25:01 +090070import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
Daniel Park2884b232021-03-04 18:58:47 +090071import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
Jian Li858ccd72021-02-04 17:25:01 +090072import static org.onosproject.net.AnnotationKeys.PORT_NAME;
73
Jian Li43244382021-01-09 00:19:02 +090074/**
75 * An utility that used in KubeVirt networking app.
76 */
77public final class KubevirtNetworkingUtil {
78
79 private static final Logger log = LoggerFactory.getLogger(KubevirtNetworkingUtil.class);
80
81 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li034820d2021-01-15 16:58:48 +090082 private static final String COLON_SLASH = "://";
83 private static final String COLON = ":";
Jian Li556709c2021-02-03 17:54:28 +090084 private static final String OF_PREFIX = "of:";
Jian Li43244382021-01-09 00:19:02 +090085
Jian Lica20b712021-01-18 00:19:31 +090086 private static final String NETWORK_STATUS_KEY = "k8s.v1.cni.cncf.io/network-status";
87 private static final String NAME = "name";
88 private static final String NETWORK_PREFIX = "default/";
89 private static final String MAC = "mac";
90 private static final String IPS = "ips";
Daniel Parkbabde9c2021-03-09 13:37:42 +090091 private static final String BR_INT = "br-int";
Jian Li9557e902021-06-08 10:12:52 +090092 private static final String METADATA = "metadata";
Jian Li0c656f02021-06-07 13:32:39 +090093 private static final String STATUS = "status";
94 private static final String INTERFACES = "interfaces";
Jian Li0c656f02021-06-07 13:32:39 +090095 private static final String NODE_NAME = "nodeName";
Jian Lica20b712021-01-18 00:19:31 +090096
Jian Li43244382021-01-09 00:19:02 +090097 /**
98 * Prevents object installation from external.
99 */
100 private KubevirtNetworkingUtil() {
101 }
102
103 /**
104 * Obtains the boolean property value with specified property key name.
105 *
Daniel Park2884b232021-03-04 18:58:47 +0900106 * @param properties a collection of properties
107 * @param name key name
Jian Li43244382021-01-09 00:19:02 +0900108 * @return mapping value
109 */
110 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
111 String name) {
112 Optional<ConfigProperty> property =
113 properties.stream().filter(p -> p.name().equals(name)).findFirst();
114
115 return property.map(ConfigProperty::asBoolean).orElse(false);
116 }
117
118 /**
119 * Re-structures the OVS port name.
120 * The length of OVS port name should be not large than 15.
121 *
Daniel Park2884b232021-03-04 18:58:47 +0900122 * @param portName original port name
Jian Li43244382021-01-09 00:19:02 +0900123 * @return re-structured OVS port name
124 */
125 public static String structurePortName(String portName) {
126
127 // The size of OVS port name should not be larger than 15
128 if (portName.length() > PORT_NAME_MAX_LENGTH) {
129 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
130 }
131
132 return portName;
133 }
Jian Lif97a07e2021-01-13 18:05:00 +0900134
135 /**
136 * Generates string format based on the given string length list.
137 *
138 * @param stringLengths a list of string lengths
139 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
140 */
141 public static String genFormatString(List<Integer> stringLengths) {
142 StringBuilder fsb = new StringBuilder();
143 stringLengths.forEach(length -> {
144 fsb.append("%-");
145 fsb.append(length);
146 fsb.append("s");
147 });
148 return fsb.toString();
149 }
150
151 /**
Jian Li556709c2021-02-03 17:54:28 +0900152 * Auto generates DPID from the given name.
153 *
154 * @param name name
155 * @return auto generated DPID
156 */
157 public static String genDpidFromName(String name) {
158 if (name != null) {
159 String hexString = Integer.toHexString(name.hashCode());
160 return OF_PREFIX + Strings.padStart(hexString, 16, '0');
161 }
162
163 return null;
164 }
165
166 /**
Jian Lif97a07e2021-01-13 18:05:00 +0900167 * Prints out the JSON string in pretty format.
168 *
Daniel Park2884b232021-03-04 18:58:47 +0900169 * @param mapper Object mapper
170 * @param jsonString JSON string
Jian Lif97a07e2021-01-13 18:05:00 +0900171 * @return pretty formatted JSON string
172 */
173 public static String prettyJson(ObjectMapper mapper, String jsonString) {
174 try {
175 Object jsonObject = mapper.readValue(jsonString, Object.class);
176 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
177 } catch (IOException e) {
178 log.debug("Json string parsing exception caused by {}", e);
179 }
180 return null;
181 }
Jian Li3ba5c582021-01-14 11:30:36 +0900182
183 /**
184 * Obtains valid IP addresses of the given subnet.
185 *
186 * @param cidr CIDR
187 * @return set of IP addresses
188 */
189 public static Set<IpAddress> getSubnetIps(String cidr) {
190 SubnetUtils utils = new SubnetUtils(cidr);
191 utils.setInclusiveHostCount(false);
192 SubnetUtils.SubnetInfo info = utils.getInfo();
193 Set<String> allAddresses =
194 new HashSet<>(Arrays.asList(info.getAllAddresses()));
195
196 if (allAddresses.size() > 2) {
197 allAddresses.remove(info.getLowAddress());
198 allAddresses.remove(info.getHighAddress());
199 }
200
201 return allAddresses.stream()
202 .map(IpAddress::valueOf).collect(Collectors.toSet());
203 }
204
205 /**
206 * Calculate the broadcast address from given IP address and subnet prefix length.
207 *
Daniel Park2884b232021-03-04 18:58:47 +0900208 * @param ipAddr IP address
209 * @param prefixLength subnet prefix length
Jian Li3ba5c582021-01-14 11:30:36 +0900210 * @return broadcast address
211 */
212 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
213 String subnet = ipAddr + "/" + prefixLength;
214 SubnetUtils utils = new SubnetUtils(subnet);
215 return utils.getInfo().getBroadcastAddress();
216 }
Daniel Park2884b232021-03-04 18:58:47 +0900217
Jian Li034820d2021-01-15 16:58:48 +0900218 /**
219 * Generates endpoint URL by referring to scheme, ipAddress and port.
220 *
Daniel Park2884b232021-03-04 18:58:47 +0900221 * @param scheme scheme
222 * @param ipAddress IP address
223 * @param port port number
Jian Li034820d2021-01-15 16:58:48 +0900224 * @return generated endpoint URL
225 */
226 public static String endpoint(KubevirtApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
227 StringBuilder endpoint = new StringBuilder();
228 String protocol = org.apache.commons.lang3.StringUtils.lowerCase(scheme.name());
229
230 endpoint.append(protocol);
231 endpoint.append(COLON_SLASH);
232 endpoint.append(ipAddress.toString());
233 endpoint.append(COLON);
234 endpoint.append(port);
235
236 return endpoint.toString();
237 }
238
239 /**
240 * Generates endpoint URL by referring to scheme, ipAddress and port.
241 *
Daniel Park2884b232021-03-04 18:58:47 +0900242 * @param apiConfig kubernetes API config
Jian Li034820d2021-01-15 16:58:48 +0900243 * @return generated endpoint URL
244 */
245 public static String endpoint(KubevirtApiConfig apiConfig) {
246 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
247 }
248
249 /**
250 * Obtains workable kubernetes client.
251 *
252 * @param config kubernetes API config
253 * @return kubernetes client
254 */
255 public static KubernetesClient k8sClient(KubevirtApiConfig config) {
256 if (config == null) {
257 log.warn("Kubernetes API server config is empty.");
258 return null;
259 }
260
261 String endpoint = endpoint(config);
262
263 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
264
265 if (config.scheme() == KubevirtApiConfig.Scheme.HTTPS) {
266 configBuilder.withTrustCerts(true)
Jian Li034820d2021-01-15 16:58:48 +0900267 .withCaCertData(config.caCertData())
268 .withClientCertData(config.clientCertData())
269 .withClientKeyData(config.clientKeyData());
270 }
271
272 return new DefaultKubernetesClient(configBuilder.build());
273 }
274
275 /**
276 * Obtains workable kubernetes client.
277 *
278 * @param service kubernetes API service
279 * @return kubernetes client
280 */
281 public static KubernetesClient k8sClient(KubevirtApiConfigService service) {
282 KubevirtApiConfig config = service.apiConfig();
283 if (config == null) {
284 log.error("Failed to find valid kubernetes API configuration.");
285 return null;
286 }
287
288 KubernetesClient client = k8sClient(config);
289
290 if (client == null) {
291 log.error("Failed to connect to kubernetes API server.");
292 return null;
293 }
294
295 return client;
296 }
Jian Lica20b712021-01-18 00:19:31 +0900297
298 /**
Jian Li556709c2021-02-03 17:54:28 +0900299 * Obtains the hex string of the given segment ID with fixed padding.
300 *
301 * @param segIdStr segment identifier string
302 * @return hex string with padding
303 */
304 public static String segmentIdHex(String segIdStr) {
305 int segId = Integer.parseInt(segIdStr);
306 return String.format("%06x", segId).toLowerCase();
307 }
308
309 /**
Jian Li858ccd72021-02-04 17:25:01 +0900310 * Obtains the tunnel port number with the given network and node.
311 *
312 * @param network kubevirt network
Daniel Park2884b232021-03-04 18:58:47 +0900313 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900314 * @return tunnel port number
315 */
316 public static PortNumber tunnelPort(KubevirtNetwork network, KubevirtNode node) {
317 switch (network.type()) {
318 case VXLAN:
319 return node.vxlanPort();
320 case GRE:
321 return node.grePort();
322 case GENEVE:
323 return node.genevePort();
324 default:
325 break;
326 }
327 return null;
328 }
329
330 /**
Jian Li0c656f02021-06-07 13:32:39 +0900331 * Obtains the kubevirt port from kubevirt VMI.
Jian Lica20b712021-01-18 00:19:31 +0900332 *
Daniel Parkf3136042021-03-10 07:49:11 +0900333 * @param nodeService kubevirt node service
Jian Lica20b712021-01-18 00:19:31 +0900334 * @param networks set of existing kubevirt networks
Jian Li0c656f02021-06-07 13:32:39 +0900335 * @param resource VMI definition
336 * @return kubevirt ports attached to the VMI
Jian Lica20b712021-01-18 00:19:31 +0900337 */
Jian Lib6dc08f2021-03-24 15:24:18 +0900338 public static Set<KubevirtPort> getPorts(KubevirtNodeService nodeService,
Jian Li0c656f02021-06-07 13:32:39 +0900339 Set<KubevirtNetwork> networks,
340 String resource) {
Jian Lica20b712021-01-18 00:19:31 +0900341 try {
Jian Li0c656f02021-06-07 13:32:39 +0900342 ObjectMapper mapper = new ObjectMapper();
343 JsonNode json = mapper.readTree(resource);
344 JsonNode statusJson = json.get(STATUS);
345 ArrayNode interfacesJson = (ArrayNode) statusJson.get(INTERFACES);
Jian Li9557e902021-06-08 10:12:52 +0900346 String vmName = parseResourceName(resource);
Jian Lib6dc08f2021-03-24 15:24:18 +0900347
Daniel Parkf3136042021-03-10 07:49:11 +0900348 KubevirtPort.Builder builder = DefaultKubevirtPort.builder();
Jian Li0c656f02021-06-07 13:32:39 +0900349 String nodeName = parseVmiNodeName(resource);
350 if (nodeName != null && nodeService.node(nodeName) != null) {
351 builder.deviceId(nodeService.node(nodeName).intgBridge());
Daniel Parkf3136042021-03-10 07:49:11 +0900352 }
Jian Lica20b712021-01-18 00:19:31 +0900353
Jian Li0c656f02021-06-07 13:32:39 +0900354 if (interfacesJson == null) {
355 return ImmutableSet.of();
356 }
Jian Lica20b712021-01-18 00:19:31 +0900357
Jian Li0c656f02021-06-07 13:32:39 +0900358 Set<KubevirtPort> ports = new HashSet<>();
359 for (JsonNode interfaceJson : interfacesJson) {
360 String name = interfaceJson.get(NAME).asText();
Jian Lica20b712021-01-18 00:19:31 +0900361 KubevirtNetwork network = networks.stream()
Jian Li0c656f02021-06-07 13:32:39 +0900362 .filter(n -> (NETWORK_PREFIX + n.name()).equals(name) ||
363 (n.name() + "-net").equals(name))
Jian Lica20b712021-01-18 00:19:31 +0900364 .findAny().orElse(null);
365 if (network != null) {
Jian Li0c656f02021-06-07 13:32:39 +0900366 // FIXME: we do not update IP address, as learning IP address
367 // requires much more time due to the lag from VM agent
368 String mac = interfaceJson.get(MAC).asText();
Jian Li9557e902021-06-08 10:12:52 +0900369 builder.vmName(vmName)
370 .macAddress(MacAddress.valueOf(mac))
Jian Lica20b712021-01-18 00:19:31 +0900371 .networkId(network.networkId());
Jian Lid4296d02021-03-12 18:03:58 +0900372 ports.add(builder.build());
Jian Lica20b712021-01-18 00:19:31 +0900373 }
374 }
Jian Lid4296d02021-03-12 18:03:58 +0900375 return ports;
Jian Li0c656f02021-06-07 13:32:39 +0900376 } catch (IOException e) {
377 log.error("Failed to parse port info from VMI object", e);
378 }
379 return ImmutableSet.of();
380 }
Jian Lid4296d02021-03-12 18:03:58 +0900381
Jian Li0c656f02021-06-07 13:32:39 +0900382 public static String parseVmiNodeName(String resource) {
383 String nodeName = null;
384 try {
385 ObjectMapper mapper = new ObjectMapper();
386 JsonNode json = mapper.readTree(resource);
387 JsonNode statusJson = json.get(STATUS);
388 JsonNode nodeNameJson = statusJson.get(NODE_NAME);
389 nodeName = nodeNameJson != null ? nodeNameJson.asText() : null;
390 } catch (IOException e) {
391 log.error("Failed to parse kubevirt VMI nodename");
Jian Lica20b712021-01-18 00:19:31 +0900392 }
393
Jian Li0c656f02021-06-07 13:32:39 +0900394 return nodeName;
Jian Lica20b712021-01-18 00:19:31 +0900395 }
Jian Li858ccd72021-02-04 17:25:01 +0900396
397 /**
398 * Obtains the tunnel bridge to tenant bridge patch port number.
399 *
Daniel Park2884b232021-03-04 18:58:47 +0900400 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900401 * @param network kubevirt network
402 * @return patch port number
403 */
404 public static PortNumber tunnelToTenantPort(KubevirtNode node, KubevirtNetwork network) {
405 if (network.segmentId() == null) {
406 return null;
407 }
408
409 if (node.tunBridge() == null) {
410 return null;
411 }
412
413 String tunToTenantPortName = TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
414 return portNumber(node.tunBridge(), tunToTenantPortName);
415 }
416
417 /**
418 * Obtains the tunnel port number of the given node.
419 *
Daniel Park2884b232021-03-04 18:58:47 +0900420 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900421 * @param network kubevirt network
422 * @return tunnel port number
423 */
424 public static PortNumber tunnelPort(KubevirtNode node, KubevirtNetwork network) {
425 if (network.segmentId() == null) {
426 return null;
427 }
428
429 if (node.tunBridge() == null) {
430 return null;
431 }
432
433 switch (network.type()) {
434 case VXLAN:
435 return node.vxlanPort();
436 case GRE:
437 return node.grePort();
438 case GENEVE:
439 return node.genevePort();
440 case FLAT:
Jian Li2ce718e2021-02-17 20:42:15 +0900441 case VLAN:
Jian Li858ccd72021-02-04 17:25:01 +0900442 default:
443 // do nothing
444 return null;
445 }
446 }
447
Jian Li810f58c2021-02-27 01:10:50 +0900448 public static String parseResourceName(String resource) {
449 try {
450 JSONObject json = new JSONObject(resource);
451 return json.getJSONObject("metadata").getString("name");
452 } catch (JSONException e) {
453 log.error("");
454 }
455 return "";
456 }
457
Daniel Parkf3136042021-03-10 07:49:11 +0900458 public static PortNumber portNumber(DeviceId deviceId, String portName) {
Jian Li858ccd72021-02-04 17:25:01 +0900459 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
460 Port port = deviceService.getPorts(deviceId).stream()
461 .filter(p -> p.isEnabled() &&
462 Objects.equals(p.annotations().value(PORT_NAME), portName))
463 .findAny().orElse(null);
464 return port != null ? port.number() : null;
465 }
466
Daniel Park2884b232021-03-04 18:58:47 +0900467 /**
468 * Returns the gateway node for the specified kubevirt router.
469 * Among gateways, only one gateway would act as a gateway per perter.
470 * Currently gateway node is selected based on modulo operation with router hashcode.
471 *
472 * @param nodeService kubevirt node service
473 * @param router kubevirt router
474 * @return elected gateway node
475 */
476 public static KubevirtNode gatewayNodeForSpecifiedRouter(KubevirtNodeService nodeService,
477 KubevirtRouter router) {
478 //TODO: enhance election logic for a better load balancing
479
480 int numOfGateways = nodeService.completeNodes(GATEWAY).size();
481 if (numOfGateways == 0) {
482 return null;
483 }
484 return (KubevirtNode) nodeService.completeNodes(GATEWAY).toArray()[router.hashCode() % numOfGateways];
485 }
486
487 /**
Daniel Parkf3136042021-03-10 07:49:11 +0900488 * Returns the mac address of the router.
489 *
490 * @param router kubevirt router
491 * @return macc address of the router
492 */
493 public static MacAddress getRouterMacAddress(KubevirtRouter router) {
494 if (router.mac() == null) {
495 log.warn("Failed to get mac address of router {}", router.name());
496 }
497
498 return router.mac();
Daniel Park2884b232021-03-04 18:58:47 +0900499 }
500
501 /**
502 * Returns the snat ip address with specified router.
503 *
504 * @param routerService router service
505 * @param internalNetworkId internal network id which is associated with the router
506 * @return snat ip address if exist, null otherwise
507 */
508 public static IpAddress getRouterSnatIpAddress(KubevirtRouterService routerService,
509 String internalNetworkId) {
510 KubevirtRouter router = routerService.routers().stream()
511 .filter(r -> r.internal().contains(internalNetworkId))
512 .findAny().orElse(null);
513
514 if (router == null) {
515 return null;
516 }
517
518 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
519
520 if (routerSnatIp == null) {
521 return null;
522 }
523
524 return Ip4Address.valueOf(routerSnatIp);
525 }
Daniel Parkbabde9c2021-03-09 13:37:42 +0900526
527 /**
528 * Returns the kubevirt router with specified kubevirt port.
529 *
530 * @param routerService kubevirt router service
531 * @param kubevirtPort kubevirt port
532 * @return kubevirt router
533 */
534 public static KubevirtRouter getRouterForKubevirtPort(KubevirtRouterService routerService,
535 KubevirtPort kubevirtPort) {
536 if (kubevirtPort.ipAddress() != null) {
537 return routerService.routers().stream()
538 .filter(r -> r.internal().contains(kubevirtPort.networkId()))
539 .findAny().orElse(null);
540 }
541 return null;
542 }
543
544 /**
545 * Returns the kubevirt router with specified kubevirt network.
546 *
547 * @param routerService kubevirt router service
548 * @param kubevirtNetwork kubevirt network
549 * @return kubevirt router
550 */
551 public static KubevirtRouter getRouterForKubevirtNetwork(KubevirtRouterService routerService,
552 KubevirtNetwork kubevirtNetwork) {
553 return routerService.routers().stream()
554 .filter(router -> router.internal().contains(kubevirtNetwork.networkId()))
555 .findAny().orElse(null);
556 }
Daniel Parkf3136042021-03-10 07:49:11 +0900557
558 /**
559 * Returns the external patch port number with specified gateway.
560 *
561 * @param deviceService device service
Jian Li9793ec42021-03-19 15:03:32 +0900562 * @param gatewayNode gateway node
Daniel Parkf3136042021-03-10 07:49:11 +0900563 * @return external patch port number
564 */
565 public static PortNumber externalPatchPortNum(DeviceService deviceService, KubevirtNode gatewayNode) {
Jian Li63f191f2021-03-25 17:14:40 +0900566 String gatewayBridgeName = gatewayNode.gatewayBridgeName();
567 if (gatewayBridgeName == null) {
Jian Li9793ec42021-03-19 15:03:32 +0900568 log.warn("No external interface is attached to gateway {}", gatewayNode.hostname());
569 return null;
570 }
571
Jian Li63f191f2021-03-25 17:14:40 +0900572 String patchPortName = "int-to-" + gatewayBridgeName;
Daniel Parkf3136042021-03-10 07:49:11 +0900573 Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
574 .filter(p -> p.isEnabled() &&
Jian Li9793ec42021-03-19 15:03:32 +0900575 Objects.equals(p.annotations().value(PORT_NAME), patchPortName))
Daniel Parkf3136042021-03-10 07:49:11 +0900576 .findAny().orElse(null);
577
578 return port != null ? port.number() : null;
579 }
580
Daniel Park157947f2021-04-09 17:50:53 +0900581 /**
582 * Returns the kubevirt external network with specified router.
583 *
584 * @param networkService kubevirt network service
585 * @param router kubevirt router
586 * @return external network
587 */
Daniel Parkf3136042021-03-10 07:49:11 +0900588 public static KubevirtNetwork getExternalNetworkByRouter(KubevirtNetworkService networkService,
589 KubevirtRouter router) {
590 String networkId = router.external().values().stream().findAny().orElse(null);
591 if (networkId == null) {
592 return null;
593 }
594
595 return networkService.network(networkId);
596 }
Daniel Park157947f2021-04-09 17:50:53 +0900597
Jian Li94b6d162021-04-15 17:09:11 +0900598 /**
599 * Resolve a DNS with the given DNS server and hostname.
600 *
601 * @param hostname hostname to be resolved
602 * @return resolved IP address
603 */
604 public static IpAddress resolveHostname(String hostname) {
605 try {
606 InetAddress addr = Address.getByName(hostname);
607 return IpAddress.valueOf(IpAddress.Version.INET, addr.getAddress());
608 } catch (UnknownHostException e) {
609 log.warn("Failed to resolve IP address of host {}", hostname);
610 }
611 return null;
612 }
613
614 /**
615 * Builds a GARP packet using the given source MAC and source IP address.
616 *
617 * @param srcMac source MAC address
618 * @param srcIp source IP address
619 * @return GARP packet
620 */
Daniel Park157947f2021-04-09 17:50:53 +0900621 public static Ethernet buildGarpPacket(MacAddress srcMac, IpAddress srcIp) {
622 if (srcMac == null || srcIp == null) {
623 return null;
624 }
625
626 Ethernet ethernet = new Ethernet();
627 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
628 ethernet.setSourceMACAddress(srcMac);
629 ethernet.setEtherType(Ethernet.TYPE_ARP);
630
631 ARP arp = new ARP();
632 arp.setOpCode(ARP.OP_REPLY);
633 arp.setProtocolType(ARP.PROTO_TYPE_IP);
634 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
635
636 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
637 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
638
639 arp.setSenderHardwareAddress(srcMac.toBytes());
640 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
641
642 arp.setSenderProtocolAddress(srcIp.toOctets());
643 arp.setTargetProtocolAddress(srcIp.toOctets());
644
645 ethernet.setPayload(arp);
646
647 return ethernet;
648 }
Daniel Park05a94582021-05-12 10:57:02 +0900649
650 /**
651 * Obtains flow group key from the given id.
652 *
653 * @param groupId flow group identifier
654 * @return flow group key
655 */
656 public static GroupKey getGroupKey(int groupId) {
657 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
658 }
659
660 /**
661 * Obtains load balancer set from the given router.
662 *
663 * @param router kubevirt router
664 * @param lbService kubevirt loadbalancer service
665 * @return loadbalancer set
666 */
667 public static Set<KubevirtLoadBalancer> getLoadBalancerSetForRouter(KubevirtRouter router,
668 KubevirtLoadBalancerService lbService) {
669
670 return lbService.loadBalancers().stream()
671 .filter(lb -> router.internal().contains(lb.networkId()))
672 .collect(Collectors.toSet());
673 }
Jian Li0c656f02021-06-07 13:32:39 +0900674
675 /**
676 * Waits for the given length of time.
677 *
678 * @param timeSecond the amount of time for wait in second unit
679 */
680 public static void waitFor(int timeSecond) {
681 try {
682 Thread.sleep(timeSecond * 1000L);
683 } catch (Exception e) {
684 log.error(e.toString());
685 }
686 }
Jian Li43244382021-01-09 00:19:02 +0900687}