blob: db54054b1e88c6d4828e0b89684e326ec76db8d2 [file] [log] [blame]
Jian Li9871cd52021-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 Li260231e2021-01-13 18:05:00 +090018import com.fasterxml.jackson.databind.ObjectMapper;
Jian Lid5e8ea82021-01-18 00:19:31 +090019import io.fabric8.kubernetes.api.model.Pod;
Jian Li16eed162021-01-15 16:58:48 +090020import io.fabric8.kubernetes.client.ConfigBuilder;
21import io.fabric8.kubernetes.client.DefaultKubernetesClient;
22import io.fabric8.kubernetes.client.KubernetesClient;
Jian Li9871cd52021-01-09 00:19:02 +090023import org.apache.commons.lang.StringUtils;
Jian Li7bca1272021-01-14 11:30:36 +090024import org.apache.commons.net.util.SubnetUtils;
Jian Lid5e8ea82021-01-18 00:19:31 +090025import org.json.JSONArray;
26import org.json.JSONException;
27import org.json.JSONObject;
Jian Li7bca1272021-01-14 11:30:36 +090028import org.onlab.packet.IpAddress;
Jian Lid5e8ea82021-01-18 00:19:31 +090029import org.onlab.packet.MacAddress;
Jian Li9871cd52021-01-09 00:19:02 +090030import org.onosproject.cfg.ConfigProperty;
Jian Lid5e8ea82021-01-18 00:19:31 +090031import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
32import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
33import org.onosproject.kubevirtnetworking.api.KubevirtPort;
Jian Li16eed162021-01-15 16:58:48 +090034import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
35import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Li9871cd52021-01-09 00:19:02 +090036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
Jian Li260231e2021-01-13 18:05:00 +090039import java.io.IOException;
Jian Li7bca1272021-01-14 11:30:36 +090040import java.util.Arrays;
41import java.util.HashSet;
Jian Li260231e2021-01-13 18:05:00 +090042import java.util.List;
Jian Lid5e8ea82021-01-18 00:19:31 +090043import java.util.Map;
Jian Li9871cd52021-01-09 00:19:02 +090044import java.util.Optional;
45import java.util.Set;
Jian Li7bca1272021-01-14 11:30:36 +090046import java.util.stream.Collectors;
Jian Li9871cd52021-01-09 00:19:02 +090047
48/**
49 * An utility that used in KubeVirt networking app.
50 */
51public final class KubevirtNetworkingUtil {
52
53 private static final Logger log = LoggerFactory.getLogger(KubevirtNetworkingUtil.class);
54
55 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li16eed162021-01-15 16:58:48 +090056 private static final String COLON_SLASH = "://";
57 private static final String COLON = ":";
Jian Li9871cd52021-01-09 00:19:02 +090058
Jian Lid5e8ea82021-01-18 00:19:31 +090059 private static final String NETWORK_STATUS_KEY = "k8s.v1.cni.cncf.io/network-status";
60 private static final String NAME = "name";
61 private static final String NETWORK_PREFIX = "default/";
62 private static final String MAC = "mac";
63 private static final String IPS = "ips";
64
Jian Li9871cd52021-01-09 00:19:02 +090065 /**
66 * Prevents object installation from external.
67 */
68 private KubevirtNetworkingUtil() {
69 }
70
71 /**
72 * Obtains the boolean property value with specified property key name.
73 *
74 * @param properties a collection of properties
75 * @param name key name
76 * @return mapping value
77 */
78 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
79 String name) {
80 Optional<ConfigProperty> property =
81 properties.stream().filter(p -> p.name().equals(name)).findFirst();
82
83 return property.map(ConfigProperty::asBoolean).orElse(false);
84 }
85
86 /**
87 * Re-structures the OVS port name.
88 * The length of OVS port name should be not large than 15.
89 *
90 * @param portName original port name
91 * @return re-structured OVS port name
92 */
93 public static String structurePortName(String portName) {
94
95 // The size of OVS port name should not be larger than 15
96 if (portName.length() > PORT_NAME_MAX_LENGTH) {
97 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
98 }
99
100 return portName;
101 }
Jian Li260231e2021-01-13 18:05:00 +0900102
103 /**
104 * Generates string format based on the given string length list.
105 *
106 * @param stringLengths a list of string lengths
107 * @return string format (e.g., %-28s%-15s%-24s%-20s%-15s)
108 */
109 public static String genFormatString(List<Integer> stringLengths) {
110 StringBuilder fsb = new StringBuilder();
111 stringLengths.forEach(length -> {
112 fsb.append("%-");
113 fsb.append(length);
114 fsb.append("s");
115 });
116 return fsb.toString();
117 }
118
119 /**
120 * Prints out the JSON string in pretty format.
121 *
122 * @param mapper Object mapper
123 * @param jsonString JSON string
124 * @return pretty formatted JSON string
125 */
126 public static String prettyJson(ObjectMapper mapper, String jsonString) {
127 try {
128 Object jsonObject = mapper.readValue(jsonString, Object.class);
129 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
130 } catch (IOException e) {
131 log.debug("Json string parsing exception caused by {}", e);
132 }
133 return null;
134 }
Jian Li7bca1272021-01-14 11:30:36 +0900135
136 /**
137 * Obtains valid IP addresses of the given subnet.
138 *
139 * @param cidr CIDR
140 * @return set of IP addresses
141 */
142 public static Set<IpAddress> getSubnetIps(String cidr) {
143 SubnetUtils utils = new SubnetUtils(cidr);
144 utils.setInclusiveHostCount(false);
145 SubnetUtils.SubnetInfo info = utils.getInfo();
146 Set<String> allAddresses =
147 new HashSet<>(Arrays.asList(info.getAllAddresses()));
148
149 if (allAddresses.size() > 2) {
150 allAddresses.remove(info.getLowAddress());
151 allAddresses.remove(info.getHighAddress());
152 }
153
154 return allAddresses.stream()
155 .map(IpAddress::valueOf).collect(Collectors.toSet());
156 }
157
158 /**
159 * Calculate the broadcast address from given IP address and subnet prefix length.
160 *
161 * @param ipAddr IP address
162 * @param prefixLength subnet prefix length
163 * @return broadcast address
164 */
165 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
166 String subnet = ipAddr + "/" + prefixLength;
167 SubnetUtils utils = new SubnetUtils(subnet);
168 return utils.getInfo().getBroadcastAddress();
169 }
Jian Li16eed162021-01-15 16:58:48 +0900170 /**
171 * Generates endpoint URL by referring to scheme, ipAddress and port.
172 *
173 * @param scheme scheme
174 * @param ipAddress IP address
175 * @param port port number
176 * @return generated endpoint URL
177 */
178 public static String endpoint(KubevirtApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
179 StringBuilder endpoint = new StringBuilder();
180 String protocol = org.apache.commons.lang3.StringUtils.lowerCase(scheme.name());
181
182 endpoint.append(protocol);
183 endpoint.append(COLON_SLASH);
184 endpoint.append(ipAddress.toString());
185 endpoint.append(COLON);
186 endpoint.append(port);
187
188 return endpoint.toString();
189 }
190
191 /**
192 * Generates endpoint URL by referring to scheme, ipAddress and port.
193 *
194 * @param apiConfig kubernetes API config
195 * @return generated endpoint URL
196 */
197 public static String endpoint(KubevirtApiConfig apiConfig) {
198 return endpoint(apiConfig.scheme(), apiConfig.ipAddress(), apiConfig.port());
199 }
200
201 /**
202 * Obtains workable kubernetes client.
203 *
204 * @param config kubernetes API config
205 * @return kubernetes client
206 */
207 public static KubernetesClient k8sClient(KubevirtApiConfig config) {
208 if (config == null) {
209 log.warn("Kubernetes API server config is empty.");
210 return null;
211 }
212
213 String endpoint = endpoint(config);
214
215 ConfigBuilder configBuilder = new ConfigBuilder().withMasterUrl(endpoint);
216
217 if (config.scheme() == KubevirtApiConfig.Scheme.HTTPS) {
218 configBuilder.withTrustCerts(true)
219 .withOauthToken(config.token())
220 .withCaCertData(config.caCertData())
221 .withClientCertData(config.clientCertData())
222 .withClientKeyData(config.clientKeyData());
223 }
224
225 return new DefaultKubernetesClient(configBuilder.build());
226 }
227
228 /**
229 * Obtains workable kubernetes client.
230 *
231 * @param service kubernetes API service
232 * @return kubernetes client
233 */
234 public static KubernetesClient k8sClient(KubevirtApiConfigService service) {
235 KubevirtApiConfig config = service.apiConfig();
236 if (config == null) {
237 log.error("Failed to find valid kubernetes API configuration.");
238 return null;
239 }
240
241 KubernetesClient client = k8sClient(config);
242
243 if (client == null) {
244 log.error("Failed to connect to kubernetes API server.");
245 return null;
246 }
247
248 return client;
249 }
Jian Lid5e8ea82021-01-18 00:19:31 +0900250
251 /**
252 * Obtains the kubevirt port from kubevirt POD.
253 *
254 * @param networks set of existing kubevirt networks
255 * @param pod kubevirt POD
256 * @return kubevirt port
257 */
258 public static KubevirtPort getPort(Set<KubevirtNetwork> networks, Pod pod) {
259 try {
260 Map<String, String> annots = pod.getMetadata().getAnnotations();
Jian Li840156c2021-01-21 20:30:52 +0900261 if (annots == null) {
262 return null;
263 }
264
Jian Li2417ab72021-02-02 17:35:12 +0900265 if (!annots.containsKey(NETWORK_STATUS_KEY)) {
266 return null;
267 }
268
Jian Lid5e8ea82021-01-18 00:19:31 +0900269 String networkStatusStr = annots.get(NETWORK_STATUS_KEY);
270
271 if (networkStatusStr == null) {
272 return null;
273 }
274
275 JSONArray networkStatus = new JSONArray(networkStatusStr);
276
277 for (int i = 0; i < networkStatus.length(); i++) {
278 JSONObject object = networkStatus.getJSONObject(i);
279 String name = object.getString(NAME);
280 KubevirtNetwork network = networks.stream()
281 .filter(n -> (NETWORK_PREFIX + n.name()).equals(name))
282 .findAny().orElse(null);
283 if (network != null) {
284 String mac = object.getString(MAC);
285
286 KubevirtPort.Builder builder = DefaultKubevirtPort.builder()
287 .macAddress(MacAddress.valueOf(mac))
288 .networkId(network.networkId());
289
290 if (object.has(IPS)) {
291 JSONArray ips = object.getJSONArray(IPS);
292 String ip = (String) ips.get(0);
293 builder.ipAddress(IpAddress.valueOf(ip));
294 }
295
296 return builder.build();
297 }
298 }
299
300 } catch (JSONException e) {
301 log.error("Failed to parse network status object", e);
302 }
303
304 return null;
305 }
Jian Li9871cd52021-01-09 00:19:02 +0900306}