blob: 82d3964cd4089d7ff9a9b19cb994fc6c14adb93a [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;
Daniel Park157947f2021-04-09 17:50:53 +090030import org.onlab.packet.ARP;
31import org.onlab.packet.Ethernet;
Daniel Park2884b232021-03-04 18:58:47 +090032import org.onlab.packet.Ip4Address;
Jian Li3ba5c582021-01-14 11:30:36 +090033import org.onlab.packet.IpAddress;
Jian Lica20b712021-01-18 00:19:31 +090034import org.onlab.packet.MacAddress;
Jian Li43244382021-01-09 00:19:02 +090035import org.onosproject.cfg.ConfigProperty;
Jian Lica20b712021-01-18 00:19:31 +090036import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
Daniel Park05a94582021-05-12 10:57:02 +090037import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
38import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerService;
Jian Lica20b712021-01-18 00:19:31 +090039import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
Daniel Parkf3136042021-03-10 07:49:11 +090040import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
Jian Lica20b712021-01-18 00:19:31 +090041import org.onosproject.kubevirtnetworking.api.KubevirtPort;
Daniel Park2884b232021-03-04 18:58:47 +090042import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
43import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
Jian Li034820d2021-01-15 16:58:48 +090044import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
45import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Li858ccd72021-02-04 17:25:01 +090046import org.onosproject.kubevirtnode.api.KubevirtNode;
Daniel Park2884b232021-03-04 18:58:47 +090047import org.onosproject.kubevirtnode.api.KubevirtNodeService;
Jian Li858ccd72021-02-04 17:25:01 +090048import org.onosproject.net.DeviceId;
49import org.onosproject.net.Port;
50import org.onosproject.net.PortNumber;
51import org.onosproject.net.device.DeviceService;
Daniel Park05a94582021-05-12 10:57:02 +090052import org.onosproject.net.group.DefaultGroupKey;
53import org.onosproject.net.group.GroupKey;
Jian Li43244382021-01-09 00:19:02 +090054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
Jian Li94b6d162021-04-15 17:09:11 +090056import org.xbill.DNS.Address;
Jian Li43244382021-01-09 00:19:02 +090057
Jian Lif97a07e2021-01-13 18:05:00 +090058import java.io.IOException;
Jian Li94b6d162021-04-15 17:09:11 +090059import java.net.InetAddress;
60import java.net.UnknownHostException;
Jian Li3ba5c582021-01-14 11:30:36 +090061import java.util.Arrays;
62import java.util.HashSet;
Jian Lif97a07e2021-01-13 18:05:00 +090063import java.util.List;
Jian Li858ccd72021-02-04 17:25:01 +090064import java.util.Objects;
Jian Li43244382021-01-09 00:19:02 +090065import java.util.Optional;
66import java.util.Set;
Jian Li3ba5c582021-01-14 11:30:36 +090067import java.util.stream.Collectors;
Jian Li43244382021-01-09 00:19:02 +090068
Jian Li858ccd72021-02-04 17:25:01 +090069import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
Daniel Park2884b232021-03-04 18:58:47 +090070import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
Jian Li858ccd72021-02-04 17:25:01 +090071import static org.onosproject.net.AnnotationKeys.PORT_NAME;
72
Jian Li43244382021-01-09 00:19:02 +090073/**
74 * An utility that used in KubeVirt networking app.
75 */
76public final class KubevirtNetworkingUtil {
77
78 private static final Logger log = LoggerFactory.getLogger(KubevirtNetworkingUtil.class);
79
80 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li034820d2021-01-15 16:58:48 +090081 private static final String COLON_SLASH = "://";
82 private static final String COLON = ":";
Jian Li556709c2021-02-03 17:54:28 +090083 private static final String OF_PREFIX = "of:";
Jian Li43244382021-01-09 00:19:02 +090084
Jian Lica20b712021-01-18 00:19:31 +090085 private static final String NETWORK_STATUS_KEY = "k8s.v1.cni.cncf.io/network-status";
86 private static final String NAME = "name";
87 private static final String NETWORK_PREFIX = "default/";
88 private static final String MAC = "mac";
89 private static final String IPS = "ips";
Daniel Parkbabde9c2021-03-09 13:37:42 +090090 private static final String BR_INT = "br-int";
Jian Li9557e902021-06-08 10:12:52 +090091 private static final String METADATA = "metadata";
Jian Li0c656f02021-06-07 13:32:39 +090092 private static final String STATUS = "status";
93 private static final String INTERFACES = "interfaces";
Jian Li0c656f02021-06-07 13:32:39 +090094 private static final String NODE_NAME = "nodeName";
Jian Lica20b712021-01-18 00:19:31 +090095
Jian Li43244382021-01-09 00:19:02 +090096 /**
97 * Prevents object installation from external.
98 */
99 private KubevirtNetworkingUtil() {
100 }
101
102 /**
103 * Obtains the boolean property value with specified property key name.
104 *
Daniel Park2884b232021-03-04 18:58:47 +0900105 * @param properties a collection of properties
106 * @param name key name
Jian Li43244382021-01-09 00:19:02 +0900107 * @return mapping value
108 */
109 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
110 String name) {
111 Optional<ConfigProperty> property =
112 properties.stream().filter(p -> p.name().equals(name)).findFirst();
113
114 return property.map(ConfigProperty::asBoolean).orElse(false);
115 }
116
117 /**
118 * Re-structures the OVS port name.
119 * The length of OVS port name should be not large than 15.
120 *
Daniel Park2884b232021-03-04 18:58:47 +0900121 * @param portName original port name
Jian Li43244382021-01-09 00:19:02 +0900122 * @return re-structured OVS port name
123 */
124 public static String structurePortName(String portName) {
125
126 // The size of OVS port name should not be larger than 15
127 if (portName.length() > PORT_NAME_MAX_LENGTH) {
128 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
129 }
130
131 return portName;
132 }
Jian Lif97a07e2021-01-13 18:05:00 +0900133
134 /**
135 * Generates string format based on the given string length list.
136 *
137 * @param stringLengths a list of string lengths
138 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
139 */
140 public static String genFormatString(List<Integer> stringLengths) {
141 StringBuilder fsb = new StringBuilder();
142 stringLengths.forEach(length -> {
143 fsb.append("%-");
144 fsb.append(length);
145 fsb.append("s");
146 });
147 return fsb.toString();
148 }
149
150 /**
Jian Li556709c2021-02-03 17:54:28 +0900151 * Auto generates DPID from the given name.
152 *
153 * @param name name
154 * @return auto generated DPID
155 */
156 public static String genDpidFromName(String name) {
157 if (name != null) {
158 String hexString = Integer.toHexString(name.hashCode());
159 return OF_PREFIX + Strings.padStart(hexString, 16, '0');
160 }
161
162 return null;
163 }
164
165 /**
Jian Lif97a07e2021-01-13 18:05:00 +0900166 * Prints out the JSON string in pretty format.
167 *
Daniel Park2884b232021-03-04 18:58:47 +0900168 * @param mapper Object mapper
169 * @param jsonString JSON string
Jian Lif97a07e2021-01-13 18:05:00 +0900170 * @return pretty formatted JSON string
171 */
172 public static String prettyJson(ObjectMapper mapper, String jsonString) {
173 try {
174 Object jsonObject = mapper.readValue(jsonString, Object.class);
175 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
176 } catch (IOException e) {
177 log.debug("Json string parsing exception caused by {}", e);
178 }
179 return null;
180 }
Jian Li3ba5c582021-01-14 11:30:36 +0900181
182 /**
183 * Obtains valid IP addresses of the given subnet.
184 *
185 * @param cidr CIDR
186 * @return set of IP addresses
187 */
188 public static Set<IpAddress> getSubnetIps(String cidr) {
189 SubnetUtils utils = new SubnetUtils(cidr);
190 utils.setInclusiveHostCount(false);
191 SubnetUtils.SubnetInfo info = utils.getInfo();
192 Set<String> allAddresses =
193 new HashSet<>(Arrays.asList(info.getAllAddresses()));
194
195 if (allAddresses.size() > 2) {
196 allAddresses.remove(info.getLowAddress());
197 allAddresses.remove(info.getHighAddress());
198 }
199
200 return allAddresses.stream()
201 .map(IpAddress::valueOf).collect(Collectors.toSet());
202 }
203
204 /**
205 * Calculate the broadcast address from given IP address and subnet prefix length.
206 *
Daniel Park2884b232021-03-04 18:58:47 +0900207 * @param ipAddr IP address
208 * @param prefixLength subnet prefix length
Jian Li3ba5c582021-01-14 11:30:36 +0900209 * @return broadcast address
210 */
211 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
212 String subnet = ipAddr + "/" + prefixLength;
213 SubnetUtils utils = new SubnetUtils(subnet);
214 return utils.getInfo().getBroadcastAddress();
215 }
Daniel Park2884b232021-03-04 18:58:47 +0900216
Jian Li034820d2021-01-15 16:58:48 +0900217 /**
218 * Generates endpoint URL by referring to scheme, ipAddress and port.
219 *
Daniel Park2884b232021-03-04 18:58:47 +0900220 * @param scheme scheme
221 * @param ipAddress IP address
222 * @param port port number
Jian Li034820d2021-01-15 16:58:48 +0900223 * @return generated endpoint URL
224 */
225 public static String endpoint(KubevirtApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
226 StringBuilder endpoint = new StringBuilder();
227 String protocol = org.apache.commons.lang3.StringUtils.lowerCase(scheme.name());
228
229 endpoint.append(protocol);
230 endpoint.append(COLON_SLASH);
231 endpoint.append(ipAddress.toString());
232 endpoint.append(COLON);
233 endpoint.append(port);
234
235 return endpoint.toString();
236 }
237
238 /**
239 * Generates endpoint URL by referring to scheme, ipAddress and port.
240 *
Daniel Park2884b232021-03-04 18:58:47 +0900241 * @param apiConfig kubernetes API config
Jian Li034820d2021-01-15 16:58:48 +0900242 * @return generated endpoint URL
243 */
244 public static String endpoint(KubevirtApiConfig apiConfig) {
245 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
246 }
247
248 /**
249 * Obtains workable kubernetes client.
250 *
251 * @param config kubernetes API config
252 * @return kubernetes client
253 */
254 public static KubernetesClient k8sClient(KubevirtApiConfig config) {
255 if (config == null) {
256 log.warn("Kubernetes API server config is empty.");
257 return null;
258 }
259
260 String endpoint = endpoint(config);
261
262 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
263
264 if (config.scheme() == KubevirtApiConfig.Scheme.HTTPS) {
265 configBuilder.withTrustCerts(true)
Jian Li034820d2021-01-15 16:58:48 +0900266 .withCaCertData(config.caCertData())
267 .withClientCertData(config.clientCertData())
268 .withClientKeyData(config.clientKeyData());
269 }
270
271 return new DefaultKubernetesClient(configBuilder.build());
272 }
273
274 /**
275 * Obtains workable kubernetes client.
276 *
277 * @param service kubernetes API service
278 * @return kubernetes client
279 */
280 public static KubernetesClient k8sClient(KubevirtApiConfigService service) {
281 KubevirtApiConfig config = service.apiConfig();
282 if (config == null) {
283 log.error("Failed to find valid kubernetes API configuration.");
284 return null;
285 }
286
287 KubernetesClient client = k8sClient(config);
288
289 if (client == null) {
290 log.error("Failed to connect to kubernetes API server.");
291 return null;
292 }
293
294 return client;
295 }
Jian Lica20b712021-01-18 00:19:31 +0900296
297 /**
Jian Li556709c2021-02-03 17:54:28 +0900298 * Obtains the hex string of the given segment ID with fixed padding.
299 *
300 * @param segIdStr segment identifier string
301 * @return hex string with padding
302 */
303 public static String segmentIdHex(String segIdStr) {
304 int segId = Integer.parseInt(segIdStr);
305 return String.format("%06x", segId).toLowerCase();
306 }
307
308 /**
Jian Li858ccd72021-02-04 17:25:01 +0900309 * Obtains the tunnel port number with the given network and node.
310 *
311 * @param network kubevirt network
Daniel Park2884b232021-03-04 18:58:47 +0900312 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900313 * @return tunnel port number
314 */
315 public static PortNumber tunnelPort(KubevirtNetwork network, KubevirtNode node) {
316 switch (network.type()) {
317 case VXLAN:
318 return node.vxlanPort();
319 case GRE:
320 return node.grePort();
321 case GENEVE:
322 return node.genevePort();
Jian Li78e82f92022-03-23 13:07:19 +0900323 case STT:
324 return node.sttPort();
Jian Li858ccd72021-02-04 17:25:01 +0900325 default:
326 break;
327 }
328 return null;
329 }
330
331 /**
Jian Li0c656f02021-06-07 13:32:39 +0900332 * Obtains the kubevirt port from kubevirt VMI.
Jian Lica20b712021-01-18 00:19:31 +0900333 *
Daniel Parkf3136042021-03-10 07:49:11 +0900334 * @param nodeService kubevirt node service
Jian Lica20b712021-01-18 00:19:31 +0900335 * @param networks set of existing kubevirt networks
Jian Li0c656f02021-06-07 13:32:39 +0900336 * @param resource VMI definition
337 * @return kubevirt ports attached to the VMI
Jian Lica20b712021-01-18 00:19:31 +0900338 */
Jian Lib6dc08f2021-03-24 15:24:18 +0900339 public static Set<KubevirtPort> getPorts(KubevirtNodeService nodeService,
Jian Li0c656f02021-06-07 13:32:39 +0900340 Set<KubevirtNetwork> networks,
341 String resource) {
Jian Lica20b712021-01-18 00:19:31 +0900342 try {
Jian Li0c656f02021-06-07 13:32:39 +0900343 ObjectMapper mapper = new ObjectMapper();
344 JsonNode json = mapper.readTree(resource);
345 JsonNode statusJson = json.get(STATUS);
346 ArrayNode interfacesJson = (ArrayNode) statusJson.get(INTERFACES);
Jian Li9557e902021-06-08 10:12:52 +0900347 String vmName = parseResourceName(resource);
Jian Lib6dc08f2021-03-24 15:24:18 +0900348
Daniel Parkf3136042021-03-10 07:49:11 +0900349 KubevirtPort.Builder builder = DefaultKubevirtPort.builder();
Jian Li0c656f02021-06-07 13:32:39 +0900350 String nodeName = parseVmiNodeName(resource);
351 if (nodeName != null && nodeService.node(nodeName) != null) {
352 builder.deviceId(nodeService.node(nodeName).intgBridge());
Daniel Parkf3136042021-03-10 07:49:11 +0900353 }
Jian Lica20b712021-01-18 00:19:31 +0900354
Jian Li0c656f02021-06-07 13:32:39 +0900355 if (interfacesJson == null) {
356 return ImmutableSet.of();
357 }
Jian Lica20b712021-01-18 00:19:31 +0900358
Jian Li0c656f02021-06-07 13:32:39 +0900359 Set<KubevirtPort> ports = new HashSet<>();
360 for (JsonNode interfaceJson : interfacesJson) {
Jian Li56f241b2021-06-30 20:59:43 +0900361 JsonNode jsonName = interfaceJson.get(NAME);
362
363 // in some cases, name attribute may not be available from the
364 // interface, we skip inspect this interface
365 if (jsonName == null) {
366 continue;
367 }
368
369 String name = jsonName.asText();
Jian Lica20b712021-01-18 00:19:31 +0900370 KubevirtNetwork network = networks.stream()
Jian Li0c656f02021-06-07 13:32:39 +0900371 .filter(n -> (NETWORK_PREFIX + n.name()).equals(name) ||
372 (n.name() + "-net").equals(name))
Jian Lica20b712021-01-18 00:19:31 +0900373 .findAny().orElse(null);
374 if (network != null) {
Jian Li0c656f02021-06-07 13:32:39 +0900375 // FIXME: we do not update IP address, as learning IP address
376 // requires much more time due to the lag from VM agent
377 String mac = interfaceJson.get(MAC).asText();
Jian Li9557e902021-06-08 10:12:52 +0900378 builder.vmName(vmName)
379 .macAddress(MacAddress.valueOf(mac))
Jian Lica20b712021-01-18 00:19:31 +0900380 .networkId(network.networkId());
Jian Lid4296d02021-03-12 18:03:58 +0900381 ports.add(builder.build());
Jian Lica20b712021-01-18 00:19:31 +0900382 }
383 }
Jian Lid4296d02021-03-12 18:03:58 +0900384 return ports;
Jian Li0c656f02021-06-07 13:32:39 +0900385 } catch (IOException e) {
386 log.error("Failed to parse port info from VMI object", e);
387 }
388 return ImmutableSet.of();
389 }
Jian Lid4296d02021-03-12 18:03:58 +0900390
Jian Li0c656f02021-06-07 13:32:39 +0900391 public static String parseVmiNodeName(String resource) {
392 String nodeName = null;
393 try {
394 ObjectMapper mapper = new ObjectMapper();
395 JsonNode json = mapper.readTree(resource);
396 JsonNode statusJson = json.get(STATUS);
397 JsonNode nodeNameJson = statusJson.get(NODE_NAME);
398 nodeName = nodeNameJson != null ? nodeNameJson.asText() : null;
399 } catch (IOException e) {
400 log.error("Failed to parse kubevirt VMI nodename");
Jian Lica20b712021-01-18 00:19:31 +0900401 }
402
Jian Li0c656f02021-06-07 13:32:39 +0900403 return nodeName;
Jian Lica20b712021-01-18 00:19:31 +0900404 }
Jian Li858ccd72021-02-04 17:25:01 +0900405
406 /**
407 * Obtains the tunnel bridge to tenant bridge patch port number.
408 *
Jian Li34fff802021-07-01 10:04:04 +0900409 * @param deviceService device service
Daniel Park2884b232021-03-04 18:58:47 +0900410 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900411 * @param network kubevirt network
412 * @return patch port number
413 */
Jian Li34fff802021-07-01 10:04:04 +0900414 public static PortNumber tunnelToTenantPort(DeviceService deviceService,
415 KubevirtNode node, KubevirtNetwork network) {
Jian Li858ccd72021-02-04 17:25:01 +0900416 if (network.segmentId() == null) {
417 return null;
418 }
419
420 if (node.tunBridge() == null) {
421 return null;
422 }
423
424 String tunToTenantPortName = TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
Jian Li34fff802021-07-01 10:04:04 +0900425 return portNumber(deviceService, node.tunBridge(), tunToTenantPortName);
Jian Li858ccd72021-02-04 17:25:01 +0900426 }
427
428 /**
429 * Obtains the tunnel port number of the given node.
430 *
Daniel Park2884b232021-03-04 18:58:47 +0900431 * @param node kubevirt node
Jian Li858ccd72021-02-04 17:25:01 +0900432 * @param network kubevirt network
433 * @return tunnel port number
434 */
435 public static PortNumber tunnelPort(KubevirtNode node, KubevirtNetwork network) {
436 if (network.segmentId() == null) {
437 return null;
438 }
439
440 if (node.tunBridge() == null) {
441 return null;
442 }
443
444 switch (network.type()) {
445 case VXLAN:
446 return node.vxlanPort();
447 case GRE:
448 return node.grePort();
449 case GENEVE:
450 return node.genevePort();
Jian Li78e82f92022-03-23 13:07:19 +0900451 case STT:
452 return node.sttPort();
Jian Li858ccd72021-02-04 17:25:01 +0900453 case FLAT:
Jian Li2ce718e2021-02-17 20:42:15 +0900454 case VLAN:
Jian Li858ccd72021-02-04 17:25:01 +0900455 default:
456 // do nothing
457 return null;
458 }
459 }
460
Jian Li810f58c2021-02-27 01:10:50 +0900461 public static String parseResourceName(String resource) {
462 try {
463 JSONObject json = new JSONObject(resource);
464 return json.getJSONObject("metadata").getString("name");
465 } catch (JSONException e) {
466 log.error("");
467 }
468 return "";
469 }
470
Jian Li34fff802021-07-01 10:04:04 +0900471 public static PortNumber portNumber(DeviceService deviceService, DeviceId deviceId, String portName) {
Jian Li858ccd72021-02-04 17:25:01 +0900472 Port port = deviceService.getPorts(deviceId).stream()
473 .filter(p -> p.isEnabled() &&
474 Objects.equals(p.annotations().value(PORT_NAME), portName))
475 .findAny().orElse(null);
476 return port != null ? port.number() : null;
477 }
478
Daniel Park2884b232021-03-04 18:58:47 +0900479 /**
480 * Returns the gateway node for the specified kubevirt router.
481 * Among gateways, only one gateway would act as a gateway per perter.
482 * Currently gateway node is selected based on modulo operation with router hashcode.
483 *
484 * @param nodeService kubevirt node service
485 * @param router kubevirt router
486 * @return elected gateway node
487 */
488 public static KubevirtNode gatewayNodeForSpecifiedRouter(KubevirtNodeService nodeService,
489 KubevirtRouter router) {
490 //TODO: enhance election logic for a better load balancing
491
492 int numOfGateways = nodeService.completeNodes(GATEWAY).size();
493 if (numOfGateways == 0) {
494 return null;
495 }
496 return (KubevirtNode) nodeService.completeNodes(GATEWAY).toArray()[router.hashCode() % numOfGateways];
497 }
498
499 /**
Daniel Parkf3136042021-03-10 07:49:11 +0900500 * Returns the mac address of the router.
501 *
502 * @param router kubevirt router
503 * @return macc address of the router
504 */
505 public static MacAddress getRouterMacAddress(KubevirtRouter router) {
506 if (router.mac() == null) {
507 log.warn("Failed to get mac address of router {}", router.name());
508 }
509
510 return router.mac();
Daniel Park2884b232021-03-04 18:58:47 +0900511 }
512
513 /**
514 * Returns the snat ip address with specified router.
515 *
516 * @param routerService router service
517 * @param internalNetworkId internal network id which is associated with the router
518 * @return snat ip address if exist, null otherwise
519 */
520 public static IpAddress getRouterSnatIpAddress(KubevirtRouterService routerService,
521 String internalNetworkId) {
522 KubevirtRouter router = routerService.routers().stream()
523 .filter(r -> r.internal().contains(internalNetworkId))
524 .findAny().orElse(null);
525
526 if (router == null) {
527 return null;
528 }
529
530 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
531
532 if (routerSnatIp == null) {
533 return null;
534 }
535
536 return Ip4Address.valueOf(routerSnatIp);
537 }
Daniel Parkbabde9c2021-03-09 13:37:42 +0900538
539 /**
540 * Returns the kubevirt router with specified kubevirt port.
541 *
542 * @param routerService kubevirt router service
543 * @param kubevirtPort kubevirt port
544 * @return kubevirt router
545 */
546 public static KubevirtRouter getRouterForKubevirtPort(KubevirtRouterService routerService,
547 KubevirtPort kubevirtPort) {
548 if (kubevirtPort.ipAddress() != null) {
549 return routerService.routers().stream()
550 .filter(r -> r.internal().contains(kubevirtPort.networkId()))
551 .findAny().orElse(null);
552 }
553 return null;
554 }
555
556 /**
557 * Returns the kubevirt router with specified kubevirt network.
558 *
559 * @param routerService kubevirt router service
560 * @param kubevirtNetwork kubevirt network
561 * @return kubevirt router
562 */
563 public static KubevirtRouter getRouterForKubevirtNetwork(KubevirtRouterService routerService,
564 KubevirtNetwork kubevirtNetwork) {
565 return routerService.routers().stream()
566 .filter(router -> router.internal().contains(kubevirtNetwork.networkId()))
567 .findAny().orElse(null);
568 }
Daniel Parkf3136042021-03-10 07:49:11 +0900569
570 /**
571 * Returns the external patch port number with specified gateway.
572 *
573 * @param deviceService device service
Jian Li9793ec42021-03-19 15:03:32 +0900574 * @param gatewayNode gateway node
Daniel Parkf3136042021-03-10 07:49:11 +0900575 * @return external patch port number
576 */
577 public static PortNumber externalPatchPortNum(DeviceService deviceService, KubevirtNode gatewayNode) {
Jian Li63f191f2021-03-25 17:14:40 +0900578 String gatewayBridgeName = gatewayNode.gatewayBridgeName();
579 if (gatewayBridgeName == null) {
Jian Li9793ec42021-03-19 15:03:32 +0900580 log.warn("No external interface is attached to gateway {}", gatewayNode.hostname());
581 return null;
582 }
583
Jian Li63f191f2021-03-25 17:14:40 +0900584 String patchPortName = "int-to-" + gatewayBridgeName;
Daniel Parkf3136042021-03-10 07:49:11 +0900585 Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
586 .filter(p -> p.isEnabled() &&
Jian Li9793ec42021-03-19 15:03:32 +0900587 Objects.equals(p.annotations().value(PORT_NAME), patchPortName))
Daniel Parkf3136042021-03-10 07:49:11 +0900588 .findAny().orElse(null);
589
590 return port != null ? port.number() : null;
591 }
592
Daniel Park157947f2021-04-09 17:50:53 +0900593 /**
594 * Returns the kubevirt external network with specified router.
595 *
596 * @param networkService kubevirt network service
597 * @param router kubevirt router
598 * @return external network
599 */
Daniel Parkf3136042021-03-10 07:49:11 +0900600 public static KubevirtNetwork getExternalNetworkByRouter(KubevirtNetworkService networkService,
601 KubevirtRouter router) {
602 String networkId = router.external().values().stream().findAny().orElse(null);
603 if (networkId == null) {
604 return null;
605 }
606
607 return networkService.network(networkId);
608 }
Daniel Park157947f2021-04-09 17:50:53 +0900609
Jian Li94b6d162021-04-15 17:09:11 +0900610 /**
611 * Resolve a DNS with the given DNS server and hostname.
612 *
613 * @param hostname hostname to be resolved
614 * @return resolved IP address
615 */
616 public static IpAddress resolveHostname(String hostname) {
617 try {
618 InetAddress addr = Address.getByName(hostname);
619 return IpAddress.valueOf(IpAddress.Version.INET, addr.getAddress());
620 } catch (UnknownHostException e) {
621 log.warn("Failed to resolve IP address of host {}", hostname);
622 }
623 return null;
624 }
625
626 /**
627 * Builds a GARP packet using the given source MAC and source IP address.
628 *
629 * @param srcMac source MAC address
630 * @param srcIp source IP address
631 * @return GARP packet
632 */
Daniel Park157947f2021-04-09 17:50:53 +0900633 public static Ethernet buildGarpPacket(MacAddress srcMac, IpAddress srcIp) {
634 if (srcMac == null || srcIp == null) {
635 return null;
636 }
637
638 Ethernet ethernet = new Ethernet();
639 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
640 ethernet.setSourceMACAddress(srcMac);
641 ethernet.setEtherType(Ethernet.TYPE_ARP);
642
643 ARP arp = new ARP();
644 arp.setOpCode(ARP.OP_REPLY);
645 arp.setProtocolType(ARP.PROTO_TYPE_IP);
646 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
647
648 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
649 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
650
651 arp.setSenderHardwareAddress(srcMac.toBytes());
652 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
653
654 arp.setSenderProtocolAddress(srcIp.toOctets());
655 arp.setTargetProtocolAddress(srcIp.toOctets());
656
657 ethernet.setPayload(arp);
658
659 return ethernet;
660 }
Daniel Park05a94582021-05-12 10:57:02 +0900661
662 /**
663 * Obtains flow group key from the given id.
664 *
665 * @param groupId flow group identifier
666 * @return flow group key
667 */
668 public static GroupKey getGroupKey(int groupId) {
669 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
670 }
671
672 /**
673 * Obtains load balancer set from the given router.
674 *
675 * @param router kubevirt router
676 * @param lbService kubevirt loadbalancer service
677 * @return loadbalancer set
678 */
679 public static Set<KubevirtLoadBalancer> getLoadBalancerSetForRouter(KubevirtRouter router,
680 KubevirtLoadBalancerService lbService) {
681
682 return lbService.loadBalancers().stream()
683 .filter(lb -> router.internal().contains(lb.networkId()))
684 .collect(Collectors.toSet());
685 }
Jian Li0c656f02021-06-07 13:32:39 +0900686
687 /**
688 * Waits for the given length of time.
689 *
690 * @param timeSecond the amount of time for wait in second unit
691 */
692 public static void waitFor(int timeSecond) {
693 try {
694 Thread.sleep(timeSecond * 1000L);
695 } catch (Exception e) {
696 log.error(e.toString());
697 }
698 }
Jian Li43244382021-01-09 00:19:02 +0900699}