blob: 48d9bc05102e4a8697e595bf6eecec7ac6b3b043 [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 Lif97a07e2021-01-13 18:05:00 +090018import com.fasterxml.jackson.databind.ObjectMapper;
Jian Li556709c2021-02-03 17:54:28 +090019import com.google.common.base.Strings;
Jian Lica20b712021-01-18 00:19:31 +090020import io.fabric8.kubernetes.api.model.Pod;
Jian Li034820d2021-01-15 16:58:48 +090021import io.fabric8.kubernetes.client.ConfigBuilder;
22import io.fabric8.kubernetes.client.DefaultKubernetesClient;
23import io.fabric8.kubernetes.client.KubernetesClient;
Jian Li43244382021-01-09 00:19:02 +090024import org.apache.commons.lang.StringUtils;
Jian Li3ba5c582021-01-14 11:30:36 +090025import org.apache.commons.net.util.SubnetUtils;
Jian Lica20b712021-01-18 00:19:31 +090026import org.json.JSONArray;
27import org.json.JSONException;
28import org.json.JSONObject;
Jian Li3ba5c582021-01-14 11:30:36 +090029import org.onlab.packet.IpAddress;
Jian Lica20b712021-01-18 00:19:31 +090030import org.onlab.packet.MacAddress;
Jian Li43244382021-01-09 00:19:02 +090031import org.onosproject.cfg.ConfigProperty;
Jian Lica20b712021-01-18 00:19:31 +090032import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
33import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
34import org.onosproject.kubevirtnetworking.api.KubevirtPort;
Jian Li034820d2021-01-15 16:58:48 +090035import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
36import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Li43244382021-01-09 00:19:02 +090037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
Jian Lif97a07e2021-01-13 18:05:00 +090040import java.io.IOException;
Jian Li3ba5c582021-01-14 11:30:36 +090041import java.util.Arrays;
42import java.util.HashSet;
Jian Lif97a07e2021-01-13 18:05:00 +090043import java.util.List;
Jian Lica20b712021-01-18 00:19:31 +090044import java.util.Map;
Jian Li43244382021-01-09 00:19:02 +090045import java.util.Optional;
46import java.util.Set;
Jian Li3ba5c582021-01-14 11:30:36 +090047import java.util.stream.Collectors;
Jian Li43244382021-01-09 00:19:02 +090048
49/**
50 * An utility that used in KubeVirt networking app.
51 */
52public final class KubevirtNetworkingUtil {
53
54 private static final Logger log = LoggerFactory.getLogger(KubevirtNetworkingUtil.class);
55
56 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li034820d2021-01-15 16:58:48 +090057 private static final String COLON_SLASH = "://";
58 private static final String COLON = ":";
Jian Li556709c2021-02-03 17:54:28 +090059 private static final String OF_PREFIX = "of:";
Jian Li43244382021-01-09 00:19:02 +090060
Jian Lica20b712021-01-18 00:19:31 +090061 private static final String NETWORK_STATUS_KEY = "k8s.v1.cni.cncf.io/network-status";
62 private static final String NAME = "name";
63 private static final String NETWORK_PREFIX = "default/";
64 private static final String MAC = "mac";
65 private static final String IPS = "ips";
66
Jian Li43244382021-01-09 00:19:02 +090067 /**
68 * Prevents object installation from external.
69 */
70 private KubevirtNetworkingUtil() {
71 }
72
73 /**
74 * Obtains the boolean property value with specified property key name.
75 *
76 * @param properties a collection of properties
77 * @param name key name
78 * @return mapping value
79 */
80 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
81 String name) {
82 Optional<ConfigProperty> property =
83 properties.stream().filter(p -> p.name().equals(name)).findFirst();
84
85 return property.map(ConfigProperty::asBoolean).orElse(false);
86 }
87
88 /**
89 * Re-structures the OVS port name.
90 * The length of OVS port name should be not large than 15.
91 *
92 * @param portName original port name
93 * @return re-structured OVS port name
94 */
95 public static String structurePortName(String portName) {
96
97 // The size of OVS port name should not be larger than 15
98 if (portName.length() > PORT_NAME_MAX_LENGTH) {
99 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
100 }
101
102 return portName;
103 }
Jian Lif97a07e2021-01-13 18:05:00 +0900104
105 /**
106 * Generates string format based on the given string length list.
107 *
108 * @param stringLengths a list of string lengths
109 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
110 */
111 public static String genFormatString(List<Integer> stringLengths) {
112 StringBuilder fsb = new StringBuilder();
113 stringLengths.forEach(length -> {
114 fsb.append("%-");
115 fsb.append(length);
116 fsb.append("s");
117 });
118 return fsb.toString();
119 }
120
121 /**
Jian Li556709c2021-02-03 17:54:28 +0900122 * Auto generates DPID from the given name.
123 *
124 * @param name name
125 * @return auto generated DPID
126 */
127 public static String genDpidFromName(String name) {
128 if (name != null) {
129 String hexString = Integer.toHexString(name.hashCode());
130 return OF_PREFIX + Strings.padStart(hexString, 16, '0');
131 }
132
133 return null;
134 }
135
136 /**
Jian Lif97a07e2021-01-13 18:05:00 +0900137 * Prints out the JSON string in pretty format.
138 *
139 * @param mapper Object mapper
140 * @param jsonString JSON string
141 * @return pretty formatted JSON string
142 */
143 public static String prettyJson(ObjectMapper mapper, String jsonString) {
144 try {
145 Object jsonObject = mapper.readValue(jsonString, Object.class);
146 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
147 } catch (IOException e) {
148 log.debug("Json string parsing exception caused by {}", e);
149 }
150 return null;
151 }
Jian Li3ba5c582021-01-14 11:30:36 +0900152
153 /**
154 * Obtains valid IP addresses of the given subnet.
155 *
156 * @param cidr CIDR
157 * @return set of IP addresses
158 */
159 public static Set<IpAddress> getSubnetIps(String cidr) {
160 SubnetUtils utils = new SubnetUtils(cidr);
161 utils.setInclusiveHostCount(false);
162 SubnetUtils.SubnetInfo info = utils.getInfo();
163 Set<String> allAddresses =
164 new HashSet<>(Arrays.asList(info.getAllAddresses()));
165
166 if (allAddresses.size() > 2) {
167 allAddresses.remove(info.getLowAddress());
168 allAddresses.remove(info.getHighAddress());
169 }
170
171 return allAddresses.stream()
172 .map(IpAddress::valueOf).collect(Collectors.toSet());
173 }
174
175 /**
176 * Calculate the broadcast address from given IP address and subnet prefix length.
177 *
178 * @param ipAddr IP address
179 * @param prefixLength subnet prefix length
180 * @return broadcast address
181 */
182 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
183 String subnet = ipAddr + "/" + prefixLength;
184 SubnetUtils utils = new SubnetUtils(subnet);
185 return utils.getInfo().getBroadcastAddress();
186 }
Jian Li034820d2021-01-15 16:58:48 +0900187 /**
188 * Generates endpoint URL by referring to scheme, ipAddress and port.
189 *
190 * @param scheme scheme
191 * @param ipAddress IP address
192 * @param port port number
193 * @return generated endpoint URL
194 */
195 public static String endpoint(KubevirtApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
196 StringBuilder endpoint = new StringBuilder();
197 String protocol = org.apache.commons.lang3.StringUtils.lowerCase(scheme.name());
198
199 endpoint.append(protocol);
200 endpoint.append(COLON_SLASH);
201 endpoint.append(ipAddress.toString());
202 endpoint.append(COLON);
203 endpoint.append(port);
204
205 return endpoint.toString();
206 }
207
208 /**
209 * Generates endpoint URL by referring to scheme, ipAddress and port.
210 *
211 * @param apiConfig kubernetes API config
212 * @return generated endpoint URL
213 */
214 public static String endpoint(KubevirtApiConfig apiConfig) {
215 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
216 }
217
218 /**
219 * Obtains workable kubernetes client.
220 *
221 * @param config kubernetes API config
222 * @return kubernetes client
223 */
224 public static KubernetesClient k8sClient(KubevirtApiConfig config) {
225 if (config == null) {
226 log.warn("Kubernetes API server config is empty.");
227 return null;
228 }
229
230 String endpoint = endpoint(config);
231
232 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
233
234 if (config.scheme() == KubevirtApiConfig.Scheme.HTTPS) {
235 configBuilder.withTrustCerts(true)
236 .withOauthToken(config.token())
237 .withCaCertData(config.caCertData())
238 .withClientCertData(config.clientCertData())
239 .withClientKeyData(config.clientKeyData());
240 }
241
242 return new DefaultKubernetesClient(configBuilder.build());
243 }
244
245 /**
246 * Obtains workable kubernetes client.
247 *
248 * @param service kubernetes API service
249 * @return kubernetes client
250 */
251 public static KubernetesClient k8sClient(KubevirtApiConfigService service) {
252 KubevirtApiConfig config = service.apiConfig();
253 if (config == null) {
254 log.error("Failed to find valid kubernetes API configuration.");
255 return null;
256 }
257
258 KubernetesClient client = k8sClient(config);
259
260 if (client == null) {
261 log.error("Failed to connect to kubernetes API server.");
262 return null;
263 }
264
265 return client;
266 }
Jian Lica20b712021-01-18 00:19:31 +0900267
268 /**
Jian Li556709c2021-02-03 17:54:28 +0900269 * Obtains the hex string of the given segment ID with fixed padding.
270 *
271 * @param segIdStr segment identifier string
272 * @return hex string with padding
273 */
274 public static String segmentIdHex(String segIdStr) {
275 int segId = Integer.parseInt(segIdStr);
276 return String.format("%06x", segId).toLowerCase();
277 }
278
279 /**
Jian Lica20b712021-01-18 00:19:31 +0900280 * Obtains the kubevirt port from kubevirt POD.
281 *
282 * @param networks set of existing kubevirt networks
283 * @param pod kubevirt POD
284 * @return kubevirt port
285 */
286 public static KubevirtPort getPort(Set<KubevirtNetwork> networks, Pod pod) {
287 try {
288 Map<String, String> annots = pod.getMetadata().getAnnotations();
Jian Li6e66a302021-01-21 20:30:52 +0900289 if (annots == null) {
290 return null;
291 }
292
Jian Li7a581b12021-02-18 14:24:32 +0900293 if (!annots.containsKey(NETWORK_STATUS_KEY)) {
294 return null;
295 }
296
Jian Lica20b712021-01-18 00:19:31 +0900297 String networkStatusStr = annots.get(NETWORK_STATUS_KEY);
298
299 if (networkStatusStr == null) {
300 return null;
301 }
302
303 JSONArray networkStatus = new JSONArray(networkStatusStr);
304
305 for (int i = 0; i < networkStatus.length(); i++) {
306 JSONObject object = networkStatus.getJSONObject(i);
307 String name = object.getString(NAME);
308 KubevirtNetwork network = networks.stream()
309 .filter(n -> (NETWORK_PREFIX + n.name()).equals(name))
310 .findAny().orElse(null);
311 if (network != null) {
312 String mac = object.getString(MAC);
313
314 KubevirtPort.Builder builder = DefaultKubevirtPort.builder()
315 .macAddress(MacAddress.valueOf(mac))
316 .networkId(network.networkId());
317
318 if (object.has(IPS)) {
319 JSONArray ips = object.getJSONArray(IPS);
320 String ip = (String) ips.get(0);
321 builder.ipAddress(IpAddress.valueOf(ip));
322 }
323
324 return builder.build();
325 }
326 }
327
328 } catch (JSONException e) {
329 log.error("Failed to parse network status object", e);
330 }
331
332 return null;
333 }
Jian Li43244382021-01-09 00:19:02 +0900334}