blob: e1edb6d8701c94659c021dc9c5043e13ba3b80c7 [file] [log] [blame]
Jian Li091d8d22018-02-20 10:42:06 +09001/*
2 * Copyright 2018-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.openstacknetworking.util;
17
Daniel Park2ff66b42018-08-01 11:52:45 +090018import com.fasterxml.jackson.core.JsonParseException;
19import com.fasterxml.jackson.core.JsonProcessingException;
20import com.fasterxml.jackson.databind.JsonMappingException;
Jian Li091d8d22018-02-20 10:42:06 +090021import com.fasterxml.jackson.databind.JsonNode;
22import com.fasterxml.jackson.databind.ObjectMapper;
Jian Lieb9f77d2018-02-20 11:25:45 +090023import com.fasterxml.jackson.databind.node.ObjectNode;
Jian Li24ec59f2018-05-23 19:01:25 +090024import com.google.common.base.Strings;
Jian Li63430202018-08-30 16:24:09 +090025import org.apache.commons.codec.binary.Hex;
26import org.apache.http.HttpException;
27import org.apache.http.HttpRequest;
28import org.apache.http.HttpResponse;
29import org.apache.http.impl.io.DefaultHttpRequestParser;
30import org.apache.http.impl.io.DefaultHttpRequestWriter;
31import org.apache.http.impl.io.DefaultHttpResponseParser;
32import org.apache.http.impl.io.DefaultHttpResponseWriter;
33import org.apache.http.impl.io.HttpTransportMetricsImpl;
34import org.apache.http.impl.io.SessionInputBufferImpl;
35import org.apache.http.impl.io.SessionOutputBufferImpl;
36import org.apache.http.io.HttpMessageWriter;
Jian Li7f70bb72018-07-06 23:35:30 +090037import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090038import org.onosproject.net.DeviceId;
Daniel Park95f73312018-07-31 15:48:34 +090039import org.onosproject.net.device.DeviceService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090040import org.onosproject.openstacknetworking.api.Constants.VnicType;
Jian Lia171a432018-06-11 11:52:11 +090041import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090042import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090043import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liec5c32b2018-07-13 14:28:58 +090044import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090045import org.onosproject.openstacknode.api.OpenstackAuth;
46import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090047import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li51b844c2018-05-31 10:59:03 +090048import org.openstack4j.api.OSClient;
49import org.openstack4j.api.client.IOSClientBuilder;
50import org.openstack4j.api.exceptions.AuthenticationException;
51import org.openstack4j.api.types.Facing;
52import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090053import org.openstack4j.core.transport.ObjectMapperSingleton;
54import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090055import org.openstack4j.model.common.Identifier;
Jian Li24ec59f2018-05-23 19:01:25 +090056import org.openstack4j.model.network.NetFloatingIP;
57import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090058import org.openstack4j.model.network.Port;
Jian Li0b564282018-06-20 00:50:53 +090059import org.openstack4j.model.network.RouterInterface;
Jian Li51b844c2018-05-31 10:59:03 +090060import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090061import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090062import org.slf4j.Logger;
63import org.slf4j.LoggerFactory;
64
Jian Li63430202018-08-30 16:24:09 +090065import javax.crypto.Mac;
66import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090067import javax.net.ssl.HostnameVerifier;
68import javax.net.ssl.HttpsURLConnection;
69import javax.net.ssl.SSLContext;
70import javax.net.ssl.TrustManager;
71import javax.net.ssl.X509TrustManager;
Jian Li63430202018-08-30 16:24:09 +090072import java.io.ByteArrayInputStream;
73import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +090074import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +090075import java.io.InputStream;
Jian Li51b844c2018-05-31 10:59:03 +090076import java.security.cert.X509Certificate;
Jian Li1064e4f2018-05-29 16:16:53 +090077import java.util.HashMap;
78import java.util.Iterator;
79import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +090080import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +090081import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +090082import java.util.Set;
83import java.util.TreeMap;
Jian Li091d8d22018-02-20 10:42:06 +090084
85import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +090086import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +090087import static com.google.common.base.Strings.isNullOrEmpty;
Daniel Park95f73312018-07-31 15:48:34 +090088import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parkc4d06402018-05-28 15:57:37 +090089import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
90import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +090091import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
92import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Daniel Parkec9d1132018-08-19 11:18:03 +090093import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -070094import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +090095import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +090096
97/**
98 * An utility that used in openstack networking app.
99 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900100public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900101
Daniel Park95985382018-07-23 11:38:07 +0900102 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900103
Daniel Parkc4d06402018-05-28 15:57:37 +0900104 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900105 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900106 private static final String PREFIX_DEVICE_NUMBER = "s";
107 private static final String PREFIX_FUNCTION_NUMBER = "f";
108
Jian Li51b844c2018-05-31 10:59:03 +0900109 // keystone endpoint related variables
110 private static final String DOMAIN_DEFAULT = "default";
111 private static final String KEYSTONE_V2 = "v2.0";
112 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900113 private static final String SSL_TYPE = "SSL";
114
Jian Li7f024de2018-07-07 03:51:02 +0900115 private static final String PROXY_MODE = "proxy";
116 private static final String BROADCAST_MODE = "broadcast";
117
Jian Licad36c72018-09-13 17:44:54 +0900118 private static final String ENABLE = "enable";
119 private static final String DISABLE = "disable";
120
Jian Li63430202018-08-30 16:24:09 +0900121 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
122
123 private static final String HMAC_SHA256 = "HmacSHA256";
124
Jian Li24ec59f2018-05-23 19:01:25 +0900125 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
126
Jian Li091d8d22018-02-20 10:42:06 +0900127 /**
128 * Prevents object instantiation from external.
129 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900130 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900131 }
132
133 /**
134 * Interprets JSON string to corresponding openstack model entity object.
135 *
136 * @param input JSON string
137 * @param entityClazz openstack model entity class
138 * @return openstack model entity object
139 */
140 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
141 ObjectMapper mapper = new ObjectMapper();
142 try {
143 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
144 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
145 return ObjectMapperSingleton.getContext(entityClazz)
146 .readerFor(entityClazz)
147 .readValue(jsonTree);
148 } catch (Exception e) {
149 throw new IllegalArgumentException();
150 }
151 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900152
153 /**
154 * Converts openstack model entity object into JSON object.
155 *
156 * @param entity openstack model entity object
157 * @param entityClazz openstack model entity class
158 * @return JSON object
159 */
160 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
161 ObjectMapper mapper = new ObjectMapper();
162 try {
163 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
164 .writerFor(entityClazz)
165 .writeValueAsString(entity);
166 log.trace(strModelEntity);
167 return (ObjectNode) mapper.readTree(strModelEntity.getBytes());
Daniel Park95985382018-07-23 11:38:07 +0900168 } catch (IOException e) {
169 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900170 throw new IllegalStateException();
171 }
172 }
Jian Li1064e4f2018-05-29 16:16:53 +0900173
174 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900175 * Obtains a floating IP associated with the given instance port.
176 *
177 * @param port instance port
178 * @param fips a collection of floating IPs
179 * @return associated floating IP
180 */
181 public static NetFloatingIP associatedFloatingIp(InstancePort port,
182 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900183 for (NetFloatingIP fip : fips) {
184 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
185 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900186 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900187 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
188 continue;
189 }
190 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
191 return fip;
192 }
Jian Li24ec59f2018-05-23 19:01:25 +0900193 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900194
Jian Li24ec59f2018-05-23 19:01:25 +0900195 return null;
196 }
197
198 /**
199 * Checks whether the given floating IP is associated with a VM.
200 *
201 * @param service openstack network service
202 * @param fip floating IP
203 * @return true if the given floating IP associated with a VM, false otherwise
204 */
205 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
206 NetFloatingIP fip) {
207 Port osPort = service.port(fip.getPortId());
208 if (osPort == null) {
209 return false;
210 }
211
212 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
213 Network osNet = service.network(osPort.getNetworkId());
214 if (osNet == null) {
215 final String errorFormat = ERR_FLOW + "no network(%s) exists";
216 final String error = String.format(errorFormat,
217 fip.getFloatingIpAddress(), osPort.getNetworkId());
218 throw new IllegalStateException(error);
219 }
220 return true;
221 } else {
222 return false;
223 }
224 }
225
226 /**
Jian Lia171a432018-06-11 11:52:11 +0900227 * Obtains the gateway node by instance port.
228 *
229 * @param gateways a collection of gateway nodes
230 * @param instPort instance port
231 * @return a gateway node
232 */
233 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
234 InstancePort instPort) {
235 OpenstackNode gw = null;
236 if (instPort != null && instPort.deviceId() != null) {
237 gw = getGwByComputeDevId(gateways, instPort.deviceId());
238 }
239 return gw;
240 }
241
242 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900243 * Obtains the gateway node by device in compute node. Note that the gateway
244 * node is determined by device's device identifier.
245 *
246 * @param gws a collection of gateway nodes
247 * @param deviceId device identifier
248 * @return a gateway node
249 */
250 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws, DeviceId deviceId) {
251 int numOfGw = gws.size();
252
253 if (numOfGw == 0) {
254 return null;
255 }
256
257 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
258
259 return getGwByIndex(gws, gwIndex);
260 }
261
Jian Li51b844c2018-05-31 10:59:03 +0900262 /**
263 * Obtains a connected openstack client.
264 *
265 * @param osNode openstack node
266 * @return a connected openstack client
267 */
268 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900269 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900270 String endpoint = buildEndpoint(osNode);
271 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900272
Jian Li51b844c2018-05-31 10:59:03 +0900273 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900274
Jian Li51b844c2018-05-31 10:59:03 +0900275 try {
276 if (endpoint.contains(KEYSTONE_V2)) {
277 IOSClientBuilder.V2 builder = OSFactory.builderV2()
278 .endpoint(endpoint)
279 .tenantName(auth.project())
280 .credentials(auth.username(), auth.password())
281 .withConfig(config);
282
283 if (perspective != null) {
284 builder.perspective(getFacing(perspective));
285 }
286
287 return builder.authenticate();
288 } else if (endpoint.contains(KEYSTONE_V3)) {
289
290 Identifier project = Identifier.byName(auth.project());
291 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
292
293 IOSClientBuilder.V3 builder = OSFactory.builderV3()
294 .endpoint(endpoint)
295 .credentials(auth.username(), auth.password(), domain)
296 .scopeToProject(project, domain)
297 .withConfig(config);
298
299 if (perspective != null) {
300 builder.perspective(getFacing(perspective));
301 }
302
303 return builder.authenticate();
304 } else {
305 log.warn("Unrecognized keystone version type");
306 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900307 }
Jian Li51b844c2018-05-31 10:59:03 +0900308 } catch (AuthenticationException e) {
309 log.error("Authentication failed due to {}", e.toString());
310 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900311 }
Jian Li1064e4f2018-05-29 16:16:53 +0900312 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900313
314 /**
315 * Extract the interface name with the supplied port.
316 *
317 * @param port port
318 * @return interface name
319 */
320 public static String getIntfNameFromPciAddress(Port port) {
Daniel Park95985382018-07-23 11:38:07 +0900321 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900322 log.error("Port profile is not found");
323 return null;
324 }
325
Daniel Park95985382018-07-23 11:38:07 +0900326 if (!port.getProfile().containsKey(PCISLOT) ||
327 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900328 log.error("Failed to retrieve the interface name because of no pci_slot information from the port");
329 return null;
330 }
Jian Li51b844c2018-05-31 10:59:03 +0900331
Daniel Parkc4d06402018-05-28 15:57:37 +0900332 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
333 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
334
335 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
336 .split(":")[2]
337 .split("\\.")[0];
338 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
339
340 String functionNumHex = port.getProfile().get(PCISLOT).toString()
341 .split(":")[2]
342 .split("\\.")[1];
343 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
344
345 String intfName;
346
347 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
348
Daniel Park95985382018-07-23 11:38:07 +0900349 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
Daniel Parkec9d1132018-08-19 11:18:03 +0900350 log.warn("Failed to retrieve the interface name because of unsupported prefix for vendor ID {}",
Daniel Park95985382018-07-23 11:38:07 +0900351 vendorInfoForPort);
Daniel Parkec9d1132018-08-19 11:18:03 +0900352 return UNSUPPORTED_VENDOR;
Daniel Parkc4d06402018-05-28 15:57:37 +0900353 }
Ray Milkey9dc57392018-06-08 08:52:31 -0700354 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
Jian Li51b844c2018-05-31 10:59:03 +0900355
Daniel Parkc4d06402018-05-28 15:57:37 +0900356 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
357 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
358 } else {
359 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
360 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
361 }
362
363 return intfName;
364 }
Jian Li51b844c2018-05-31 10:59:03 +0900365
366 /**
Daniel Park95f73312018-07-31 15:48:34 +0900367 * Check if the given interface is added to the given device or not.
368 *
369 * @param deviceId device ID
370 * @param intfName interface name
371 * @param deviceService device service
372 * @return true if the given interface is added to the given device or false otherwise
373 */
374 public static boolean hasIntfAleadyInDevice(DeviceId deviceId, String intfName, DeviceService deviceService) {
375 checkNotNull(deviceId);
376 checkNotNull(intfName);
377
378 return deviceService.getPorts(deviceId).stream()
379 .anyMatch(port -> Objects.equals(port.annotations().value(PORT_NAME), intfName));
380 }
381
382 /**
Jian Li0b564282018-06-20 00:50:53 +0900383 * Adds router interfaces to openstack admin service.
384 * TODO fix the logic to add router interface to router
385 *
386 * @param osPort port
387 * @param adminService openstack admin service
388 */
389 public static void addRouterIface(Port osPort, OpenstackRouterAdminService adminService) {
390 osPort.getFixedIps().forEach(p -> {
391 JsonNode jsonTree = new ObjectMapper().createObjectNode()
392 .put("id", osPort.getDeviceId())
393 .put("tenant_id", osPort.getTenantId())
394 .put("subnet_id", p.getSubnetId())
395 .put("port_id", osPort.getId());
396 try {
397 RouterInterface rIface = getContext(NeutronRouterInterface.class)
398 .readerFor(NeutronRouterInterface.class)
399 .readValue(jsonTree);
400 if (adminService.routerInterface(rIface.getPortId()) != null) {
401 adminService.updateRouterInterface(rIface);
402 } else {
403 adminService.addRouterInterface(rIface);
404 }
405 } catch (IOException ignore) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900406 log.error("Exception occurred because of {}", ignore.toString());
Jian Li0b564282018-06-20 00:50:53 +0900407 }
408 });
409 }
410
411 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900412 * Obtains the property value with specified property key name.
413 *
414 * @param properties a collection of properties
415 * @param name key name
416 * @return mapping value
417 */
418 public static String getPropertyValue(Set<ConfigProperty> properties, String name) {
419 Optional<ConfigProperty> property =
420 properties.stream().filter(p -> p.name().equals(name)).findFirst();
421 return property.map(ConfigProperty::value).orElse(null);
422 }
423
424 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900425 * Prints out the JSON string in pretty format.
426 *
427 * @param mapper Object mapper
428 * @param jsonString JSON string
429 * @return pretty formatted JSON string
430 */
431 public static String prettyJson(ObjectMapper mapper, String jsonString) {
432 try {
433 Object jsonObject = mapper.readValue(jsonString, Object.class);
434 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900435 } catch (JsonParseException e) {
436 log.debug("JsonParseException caused by {}", e);
437 } catch (JsonMappingException e) {
438 log.debug("JsonMappingException caused by {}", e);
439 } catch (JsonProcessingException e) {
440 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900441 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900442 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900443 }
444 return null;
445 }
446
447 /**
Jian Li7f024de2018-07-07 03:51:02 +0900448 * Checks the validity of ARP mode.
449 *
450 * @param arpMode ARP mode
451 * @return returns true if the ARP mode is valid, false otherwise
452 */
453 public static boolean checkArpMode(String arpMode) {
454
455 if (isNullOrEmpty(arpMode)) {
456 return false;
457 } else {
458 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
459 }
460 }
461
462 /**
Jian Licad36c72018-09-13 17:44:54 +0900463 * Checks the validity of activation flag.
464 *
465 * @param activationFlag activation flag
466 * @return returns true if the activation flag is valid, false otherwise
467 */
468 public static boolean checkActivationFlag(String activationFlag) {
469
470 switch (activationFlag) {
471 case ENABLE:
472 return true;
473 case DISABLE:
474 return false;
475 default:
476 throw new IllegalArgumentException("The given activation flag is not valid!");
477 }
478 }
479
480 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900481 * Swaps current location with old location info.
482 * The revised instance port will be used to mod the flow rules after migration.
483 *
484 * @param instPort instance port
485 * @return location swapped instance port
486 */
487 public static InstancePort swapStaleLocation(InstancePort instPort) {
488 return DefaultInstancePort.builder()
489 .deviceId(instPort.oldDeviceId())
490 .portNumber(instPort.oldPortNumber())
491 .state(instPort.state())
492 .ipAddress(instPort.ipAddress())
493 .macAddress(instPort.macAddress())
494 .networkId(instPort.networkId())
495 .portId(instPort.portId())
496 .build();
497 }
498
499 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900500 * Compares two router interfaces are equal.
501 * Will be remove this after Openstack4j implements equals.
502 *
503 * @param routerInterface1 router interface
504 * @param routerInterface2 router interface
505 * @return returns true if two router interfaces are equal, false otherwise
506 */
Jian Li63430202018-08-30 16:24:09 +0900507 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
508 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900509 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
510 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
511 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
512 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
513 }
514
Daniel Park7e8c4d82018-08-13 23:47:49 +0900515 public static VnicType vnicType(String portName) {
516 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
517 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
518 return VnicType.NORMAL;
519 } else if (isDirectPort(portName)) {
520 return VnicType.DIRECT;
521 } else {
522 return VnicType.UNSUPPORTED;
523 }
524 }
525
Jian Li63430202018-08-30 16:24:09 +0900526 /**
527 * Deserializes raw payload into HttpRequest object.
528 *
529 * @param rawData raw http payload
530 * @return HttpRequest object
531 */
532 public static HttpRequest parseHttpRequest(byte[] rawData) {
533 SessionInputBufferImpl sessionInputBuffer =
534 new SessionInputBufferImpl(
535 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
536 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
537 DefaultHttpRequestParser requestParser = new DefaultHttpRequestParser(sessionInputBuffer);
538 try {
539 return requestParser.parse();
540 } catch (IOException | HttpException e) {
541 log.warn("Failed to parse HttpRequest, due to {}", e);
542 }
543
544 return null;
545 }
546
547 /**
548 * Serializes HttpRequest object to byte array.
549 *
550 * @param request http request object
551 * @return byte array
552 */
553 public static byte[] unparseHttpRequest(HttpRequest request) {
554 try {
555 SessionOutputBufferImpl sessionOutputBuffer =
556 new SessionOutputBufferImpl(
557 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
558
559 ByteArrayOutputStream baos = new ByteArrayOutputStream();
560 sessionOutputBuffer.bind(baos);
561
562 HttpMessageWriter<HttpRequest> requestWriter = new DefaultHttpRequestWriter(
563 sessionOutputBuffer);
564 requestWriter.write(request);
565 sessionOutputBuffer.flush();
566
567 return baos.toByteArray();
568 } catch (HttpException | IOException e) {
569 log.warn("Failed to unparse HttpRequest, due to {}", e);
570 }
571
572 return null;
573 }
574
575 /**
576 * Deserializes raw payload into HttpResponse object.
577 *
578 * @param rawData raw http payload
579 * @return HttpResponse object
580 */
581 public static HttpResponse parseHttpResponse(byte[] rawData) {
582 SessionInputBufferImpl sessionInputBuffer =
583 new SessionInputBufferImpl(
584 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
585 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
586 DefaultHttpResponseParser responseParser = new DefaultHttpResponseParser(sessionInputBuffer);
587 try {
588 return responseParser.parse();
589 } catch (IOException | HttpException e) {
590 log.warn("Failed to parse HttpResponse, due to {}", e);
591 }
592
593 return null;
594 }
595
596 /**
597 * Serializes HttpResponse header to byte array.
598 *
599 * @param response http response object
600 * @return byte array
601 */
602 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
603 try {
604 SessionOutputBufferImpl sessionOutputBuffer =
605 new SessionOutputBufferImpl(
606 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
607
608 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
609 sessionOutputBuffer.bind(headerBaos);
610
611 HttpMessageWriter<HttpResponse> responseWriter =
612 new DefaultHttpResponseWriter(sessionOutputBuffer);
613 responseWriter.write(response);
614 sessionOutputBuffer.flush();
615
616 log.debug(headerBaos.toString());
617
618 return headerBaos.toByteArray();
619 } catch (IOException | HttpException e) {
620 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
621 }
622
623 return null;
624 }
625
626 /**
627 * Serializes HttpResponse object to byte array.
628 *
629 * @param response http response object
630 * @return byte array
631 */
632 public static byte[] unparseHttpResponseBody(HttpResponse response) {
633 try {
634 ByteArrayOutputStream baos = new ByteArrayOutputStream();
635 response.getEntity().writeTo(baos);
636
637 log.debug(response.toString());
638 log.debug(baos.toString());
639
640 return baos.toByteArray();
641 } catch (IOException e) {
642 log.warn("Failed to unparse HttpResponse, due to {}", e);
643 }
644
645 return null;
646 }
647
648 /**
649 * Encodes the given data using HmacSHA256 encryption method with given secret key.
650 *
651 * @param key secret key
652 * @param data data to be encrypted
653 * @return Hmac256 encrypted data
654 */
655 public static String hmacEncrypt(String key, String data) {
656 try {
657 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
658 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
659 sha256Hmac.init(secretKey);
660 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
661 } catch (Exception e) {
662 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
663 }
664 return null;
665 }
666
Daniel Park7e8c4d82018-08-13 23:47:49 +0900667 private static boolean isDirectPort(String portName) {
Daniel Parkec9d1132018-08-19 11:18:03 +0900668 return portNamePrefixMap().values().stream().anyMatch(p -> portName.startsWith(p));
Daniel Park7e8c4d82018-08-13 23:47:49 +0900669 }
670
Daniel Park2ff66b42018-08-01 11:52:45 +0900671 /**
Jian Li51b844c2018-05-31 10:59:03 +0900672 * Builds up and a complete endpoint URL from gateway node.
673 *
674 * @param node gateway node
675 * @return a complete endpoint URL
676 */
677 private static String buildEndpoint(OpenstackNode node) {
678
Jian Lic704b672018-09-04 18:52:53 +0900679 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900680
681 StringBuilder endpointSb = new StringBuilder();
682 endpointSb.append(auth.protocol().name().toLowerCase());
683 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +0900684 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +0900685 return endpointSb.toString();
686 }
687
688 /**
689 * Obtains the SSL config without verifying the certification.
690 *
691 * @return SSL config
692 */
693 private static Config getSslConfig() {
694 // we bypass the SSL certification verification for now
695 // TODO: verify server side SSL using a given certification
696 Config config = Config.newConfig().withSSLVerificationDisabled();
697
698 TrustManager[] trustAllCerts = new TrustManager[]{
699 new X509TrustManager() {
700 public X509Certificate[] getAcceptedIssuers() {
701 return null;
702 }
703
704 public void checkClientTrusted(X509Certificate[] certs,
705 String authType) {
706 }
707
708 public void checkServerTrusted(X509Certificate[] certs,
709 String authType) {
710 }
711 }
712 };
713
714 HostnameVerifier allHostsValid = (hostname, session) -> true;
715
716 try {
717 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
718 sc.init(null, trustAllCerts,
719 new java.security.SecureRandom());
720 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
721 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
722
723 config.withSSLContext(sc);
724 } catch (Exception e) {
725 log.error("Failed to access OpenStack service due to {}", e.toString());
726 return null;
727 }
728
729 return config;
730 }
731
732 /**
733 * Obtains the facing object with given openstack perspective.
734 *
735 * @param perspective keystone perspective
736 * @return facing object
737 */
738 private static Facing getFacing(Perspective perspective) {
739
740 switch (perspective) {
741 case PUBLIC:
742 return Facing.PUBLIC;
743 case ADMIN:
744 return Facing.ADMIN;
745 case INTERNAL:
746 return Facing.INTERNAL;
747 default:
748 return null;
749 }
750 }
751
752 /**
753 * Obtains gateway instance by giving index number.
754 *
755 * @param gws a collection of gateway nodes
756 * @param index index number
757 * @return gateway instance
758 */
759 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
760 Map<String, OpenstackNode> hashMap = new HashMap<>();
761 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
762 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
763 Iterator<String> iteratorKey = treeMap.keySet().iterator();
764
765 int intIndex = 0;
766 OpenstackNode gw = null;
767 while (iteratorKey.hasNext()) {
768 String key = iteratorKey.next();
769
770 if (intIndex == index) {
771 gw = treeMap.get(key);
772 }
773 intIndex++;
774 }
775 return gw;
776 }
Jian Li63430202018-08-30 16:24:09 +0900777}