blob: 2be23e3d6f1e7c592d475f706d8354444e251e38 [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 Li1951db72020-05-11 13:05:47 +090023import com.fasterxml.jackson.databind.node.ArrayNode;
Jian Lieb9f77d2018-02-20 11:25:45 +090024import com.fasterxml.jackson.databind.node.ObjectNode;
Daniel Parka73c2362018-09-17 17:43:25 +090025import com.google.common.base.Charsets;
Jian Li24ec59f2018-05-23 19:01:25 +090026import com.google.common.base.Strings;
Daniel Parka73c2362018-09-17 17:43:25 +090027import com.google.common.collect.Lists;
Jian Li63430202018-08-30 16:24:09 +090028import org.apache.commons.codec.binary.Hex;
Jian Li51728702019-05-17 18:38:56 +090029import org.apache.commons.lang3.StringUtils;
Jian Li6a1bcfd2020-01-30 17:41:26 +090030import org.apache.commons.net.util.SubnetUtils;
Jian Li63430202018-08-30 16:24:09 +090031import org.apache.http.HttpException;
32import org.apache.http.HttpRequest;
33import org.apache.http.HttpResponse;
34import org.apache.http.impl.io.DefaultHttpRequestParser;
35import org.apache.http.impl.io.DefaultHttpRequestWriter;
36import org.apache.http.impl.io.DefaultHttpResponseParser;
37import org.apache.http.impl.io.DefaultHttpResponseWriter;
38import org.apache.http.impl.io.HttpTransportMetricsImpl;
39import org.apache.http.impl.io.SessionInputBufferImpl;
40import org.apache.http.impl.io.SessionOutputBufferImpl;
41import org.apache.http.io.HttpMessageWriter;
Daniel Parka73c2362018-09-17 17:43:25 +090042import org.apache.sshd.client.SshClient;
43import org.apache.sshd.client.channel.ClientChannel;
44import org.apache.sshd.client.channel.ClientChannelEvent;
45import org.apache.sshd.client.future.OpenFuture;
46import org.apache.sshd.client.session.ClientSession;
47import org.apache.sshd.common.util.io.NoCloseInputStream;
Jian Li7b8c3682019-05-12 13:57:15 +090048import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090049import org.onlab.packet.ARP;
50import org.onlab.packet.Ethernet;
51import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090052import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090053import org.onlab.packet.MacAddress;
54import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090055import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090056import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090057import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090058import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090059import org.onosproject.net.flow.DefaultTrafficTreatment;
60import org.onosproject.net.flow.TrafficTreatment;
Jian Lid5727622019-09-11 11:15:16 +090061import org.onosproject.net.group.DefaultGroupKey;
62import org.onosproject.net.group.GroupKey;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090063import org.onosproject.net.packet.DefaultOutboundPacket;
64import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090065import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090066import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090067import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li7b8c3682019-05-12 13:57:15 +090068import org.onosproject.openstacknetworking.api.OpenstackHaService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090069import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090070import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090071import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090072import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090073import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090074import org.onosproject.openstacknode.api.OpenstackAuth;
75import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090076import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090077import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51728702019-05-17 18:38:56 +090078import org.onosproject.ovsdb.controller.OvsdbClientService;
79import org.onosproject.ovsdb.controller.OvsdbController;
80import org.onosproject.ovsdb.controller.OvsdbNodeId;
Jian Li51b844c2018-05-31 10:59:03 +090081import org.openstack4j.api.OSClient;
82import org.openstack4j.api.client.IOSClientBuilder;
83import org.openstack4j.api.exceptions.AuthenticationException;
84import org.openstack4j.api.types.Facing;
85import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090086import org.openstack4j.core.transport.ObjectMapperSingleton;
87import org.openstack4j.model.ModelEntity;
Jian Li5b22f112020-10-05 22:36:41 +090088import org.openstack4j.model.common.BasicResource;
Jian Li51b844c2018-05-31 10:59:03 +090089import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090090import org.openstack4j.model.network.ExternalGateway;
Jian Li40f032a2019-10-02 20:36:09 +090091import org.openstack4j.model.network.IP;
Jian Li24ec59f2018-05-23 19:01:25 +090092import org.openstack4j.model.network.NetFloatingIP;
93import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090094import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090095import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090096import org.openstack4j.model.network.RouterInterface;
Jian Li40f032a2019-10-02 20:36:09 +090097import org.openstack4j.model.network.SecurityGroup;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090098import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090099import org.openstack4j.openstack.OSFactory;
Jian Li1951db72020-05-11 13:05:47 +0900100import org.openstack4j.openstack.networking.domain.NeutronPort;
Jian Li0b564282018-06-20 00:50:53 +0900101import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +0900102import org.slf4j.Logger;
103import org.slf4j.LoggerFactory;
104
Jian Li63430202018-08-30 16:24:09 +0900105import javax.crypto.Mac;
106import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +0900107import javax.net.ssl.HostnameVerifier;
108import javax.net.ssl.HttpsURLConnection;
109import javax.net.ssl.SSLContext;
110import javax.net.ssl.TrustManager;
111import javax.net.ssl.X509TrustManager;
Jian Li7b8c3682019-05-12 13:57:15 +0900112import javax.ws.rs.client.Client;
113import javax.ws.rs.client.ClientBuilder;
114import javax.ws.rs.client.Entity;
115import javax.ws.rs.client.WebTarget;
116import javax.ws.rs.core.Response;
Jian Li63430202018-08-30 16:24:09 +0900117import java.io.ByteArrayInputStream;
118import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900119import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900120import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900121import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900122import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900123import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900124import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900125import java.util.HashMap;
126import java.util.Iterator;
Jian Li40f032a2019-10-02 20:36:09 +0900127import java.util.List;
Jian Li1064e4f2018-05-29 16:16:53 +0900128import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900129import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900130import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900131import java.util.Set;
132import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900133import java.util.concurrent.TimeUnit;
Jian Li40f032a2019-10-02 20:36:09 +0900134import java.util.stream.Collectors;
Jian Li091d8d22018-02-20 10:42:06 +0900135
136import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900137import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900138import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li7b8c3682019-05-12 13:57:15 +0900139import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
140import static org.apache.commons.io.IOUtils.toInputStream;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900141import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900142import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900143import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Licda74c82019-10-31 22:24:17 +0900144import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
Jian Li40f032a2019-10-02 20:36:09 +0900145import static org.onosproject.openstacknetworking.api.Constants.FLOATING_IP_FORMAT;
146import static org.onosproject.openstacknetworking.api.Constants.NETWORK_FORMAT;
Jian Li7b8c3682019-05-12 13:57:15 +0900147import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_REST_PATH;
Daniel Parkc4d06402018-05-28 15:57:37 +0900148import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
149import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Jian Li40f032a2019-10-02 20:36:09 +0900150import static org.onosproject.openstacknetworking.api.Constants.PORT_FORMAT;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900151import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
152import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Jian Li7b8c3682019-05-12 13:57:15 +0900153import static org.onosproject.openstacknetworking.api.Constants.REST_PASSWORD;
154import static org.onosproject.openstacknetworking.api.Constants.REST_PORT;
155import static org.onosproject.openstacknetworking.api.Constants.REST_USER;
156import static org.onosproject.openstacknetworking.api.Constants.REST_UTF8;
Jian Li40f032a2019-10-02 20:36:09 +0900157import static org.onosproject.openstacknetworking.api.Constants.ROUTER_FORMAT;
158import static org.onosproject.openstacknetworking.api.Constants.ROUTER_INTF_FORMAT;
159import static org.onosproject.openstacknetworking.api.Constants.SECURITY_GROUP_FORMAT;
160import static org.onosproject.openstacknetworking.api.Constants.SUBNET_FORMAT;
Daniel Parkec9d1132018-08-19 11:18:03 +0900161import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700162import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900163import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900164
165/**
166 * An utility that used in openstack networking app.
167 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900168public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900169
Daniel Park95985382018-07-23 11:38:07 +0900170 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900171
Daniel Parkc4d06402018-05-28 15:57:37 +0900172 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900173 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900174 private static final String PREFIX_DEVICE_NUMBER = "s";
175 private static final String PREFIX_FUNCTION_NUMBER = "f";
176
Jian Li5b22f112020-10-05 22:36:41 +0900177 private static final String PARENTHESES_START = "(";
178 private static final String PARENTHESES_END = ")";
179
Jian Li51b844c2018-05-31 10:59:03 +0900180 // keystone endpoint related variables
181 private static final String DOMAIN_DEFAULT = "default";
182 private static final String KEYSTONE_V2 = "v2.0";
183 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900184 private static final String SSL_TYPE = "SSL";
185
Jian Li7f024de2018-07-07 03:51:02 +0900186 private static final String PROXY_MODE = "proxy";
187 private static final String BROADCAST_MODE = "broadcast";
188
Jian Licad36c72018-09-13 17:44:54 +0900189 private static final String ENABLE = "enable";
190 private static final String DISABLE = "disable";
191
Jian Li63430202018-08-30 16:24:09 +0900192 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
193
194 private static final String HMAC_SHA256 = "HmacSHA256";
195
Jian Li24ec59f2018-05-23 19:01:25 +0900196 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
197
Daniel Parka73c2362018-09-17 17:43:25 +0900198 private static final String DL_DST = "dl_dst=";
199 private static final String NW_DST = "nw_dst=";
200 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
201 private static final String IN_PORT = "in_port=";
202 private static final String NW_SRC = "nw_src=";
203 private static final String COMMA = ",";
204 private static final String TUN_ID = "tun_id=";
205
Jian Li40f032a2019-10-02 20:36:09 +0900206 private static final String DEVICE_OWNER_GW = "network:router_gateway";
207 private static final String DEVICE_OWNER_IFACE = "network:router_interface";
208
209 private static final String NOT_AVAILABLE = "N/A";
210
Daniel Parka73c2362018-09-17 17:43:25 +0900211 private static final long TIMEOUT_MS = 5000;
212 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
213 private static final int SSH_PORT = 22;
214
Jian Li51728702019-05-17 18:38:56 +0900215 private static final int TAP_PORT_LENGTH = 11;
Jian Lia271b3c2019-09-03 23:10:20 +0900216 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li51728702019-05-17 18:38:56 +0900217
Jian Li091d8d22018-02-20 10:42:06 +0900218 /**
219 * Prevents object instantiation from external.
220 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900221 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900222 }
223
224 /**
225 * Interprets JSON string to corresponding openstack model entity object.
226 *
Jian Li7b8c3682019-05-12 13:57:15 +0900227 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900228 * @param entityClazz openstack model entity class
229 * @return openstack model entity object
230 */
Jian Li7b8c3682019-05-12 13:57:15 +0900231 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900232 ObjectMapper mapper = new ObjectMapper();
233 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900234 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900235 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
236 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
237 return ObjectMapperSingleton.getContext(entityClazz)
238 .readerFor(entityClazz)
239 .readValue(jsonTree);
240 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900241 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900242 throw new IllegalArgumentException();
243 }
244 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900245
246 /**
247 * Converts openstack model entity object into JSON object.
248 *
249 * @param entity openstack model entity object
250 * @param entityClazz openstack model entity class
251 * @return JSON object
252 */
253 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
254 ObjectMapper mapper = new ObjectMapper();
255 try {
256 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
257 .writerFor(entityClazz)
258 .writeValueAsString(entity);
259 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900260 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900261 } catch (IOException e) {
262 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900263 throw new IllegalStateException();
264 }
265 }
Jian Li1064e4f2018-05-29 16:16:53 +0900266
267 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900268 * Obtains a floating IP associated with the given instance port.
269 *
270 * @param port instance port
271 * @param fips a collection of floating IPs
272 * @return associated floating IP
273 */
274 public static NetFloatingIP associatedFloatingIp(InstancePort port,
275 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900276 for (NetFloatingIP fip : fips) {
277 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
278 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900279 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900280 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
281 continue;
282 }
Jian Li6bc29d92018-10-02 13:55:05 +0900283 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
284 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900285 return fip;
286 }
Jian Li24ec59f2018-05-23 19:01:25 +0900287 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900288
Jian Li24ec59f2018-05-23 19:01:25 +0900289 return null;
290 }
291
292 /**
293 * Checks whether the given floating IP is associated with a VM.
294 *
295 * @param service openstack network service
296 * @param fip floating IP
297 * @return true if the given floating IP associated with a VM, false otherwise
298 */
299 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
300 NetFloatingIP fip) {
301 Port osPort = service.port(fip.getPortId());
302 if (osPort == null) {
303 return false;
304 }
305
306 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
307 Network osNet = service.network(osPort.getNetworkId());
308 if (osNet == null) {
309 final String errorFormat = ERR_FLOW + "no network(%s) exists";
310 final String error = String.format(errorFormat,
311 fip.getFloatingIpAddress(), osPort.getNetworkId());
312 throw new IllegalStateException(error);
313 }
314 return true;
315 } else {
316 return false;
317 }
318 }
319
320 /**
Jian Lia171a432018-06-11 11:52:11 +0900321 * Obtains the gateway node by instance port.
322 *
323 * @param gateways a collection of gateway nodes
324 * @param instPort instance port
325 * @return a gateway node
326 */
327 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
328 InstancePort instPort) {
329 OpenstackNode gw = null;
330 if (instPort != null && instPort.deviceId() != null) {
331 gw = getGwByComputeDevId(gateways, instPort.deviceId());
332 }
333 return gw;
334 }
335
336 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900337 * Obtains the gateway node by device in compute node. Note that the gateway
338 * node is determined by device's device identifier.
339 *
340 * @param gws a collection of gateway nodes
341 * @param deviceId device identifier
342 * @return a gateway node
343 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900344 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
345 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900346 int numOfGw = gws.size();
347
348 if (numOfGw == 0) {
349 return null;
350 }
351
352 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
353
354 return getGwByIndex(gws, gwIndex);
355 }
356
Jian Li51b844c2018-05-31 10:59:03 +0900357 /**
358 * Obtains a connected openstack client.
359 *
360 * @param osNode openstack node
361 * @return a connected openstack client
362 */
363 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900364 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900365 String endpoint = buildEndpoint(osNode);
366 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900367
Jian Li51b844c2018-05-31 10:59:03 +0900368 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900369
Jian Li51b844c2018-05-31 10:59:03 +0900370 try {
371 if (endpoint.contains(KEYSTONE_V2)) {
372 IOSClientBuilder.V2 builder = OSFactory.builderV2()
373 .endpoint(endpoint)
374 .tenantName(auth.project())
375 .credentials(auth.username(), auth.password())
376 .withConfig(config);
377
378 if (perspective != null) {
379 builder.perspective(getFacing(perspective));
380 }
381
382 return builder.authenticate();
383 } else if (endpoint.contains(KEYSTONE_V3)) {
384
385 Identifier project = Identifier.byName(auth.project());
386 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
387
388 IOSClientBuilder.V3 builder = OSFactory.builderV3()
389 .endpoint(endpoint)
390 .credentials(auth.username(), auth.password(), domain)
391 .scopeToProject(project, domain)
392 .withConfig(config);
393
394 if (perspective != null) {
395 builder.perspective(getFacing(perspective));
396 }
397
398 return builder.authenticate();
399 } else {
400 log.warn("Unrecognized keystone version type");
401 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900402 }
Jian Li51b844c2018-05-31 10:59:03 +0900403 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900404 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900405 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900406 }
Jian Li1064e4f2018-05-29 16:16:53 +0900407 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900408
409 /**
Jian Licda74c82019-10-31 22:24:17 +0900410 * Checks whether the given openstack port is smart NIC capable.
411 *
412 * @param port openstack port
413 * @return true if the given port is smart NIC capable, false otherwise
414 */
415 public static boolean isSmartNicCapable(Port port) {
416 if (port.getProfile() != null && port.getvNicType().equals(DIRECT)) {
417 String vendorInfo = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
418 if (portNamePrefixMap().containsKey(vendorInfo)) {
419 log.debug("Port {} is a Smart NIC capable port.", port.getId());
420 return true;
421 }
422 return false;
423 }
424 return false;
425 }
426
427 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900428 * Extract the interface name with the supplied port.
429 *
430 * @param port port
431 * @return interface name
432 */
433 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900434 String intfName;
435
Daniel Park95985382018-07-23 11:38:07 +0900436 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900437 log.error("Port profile is not found");
438 return null;
439 }
440
Daniel Park95985382018-07-23 11:38:07 +0900441 if (!port.getProfile().containsKey(PCISLOT) ||
442 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900443 log.error("Failed to retrieve the interface name because of no " +
444 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900445 return null;
446 }
Jian Li51b844c2018-05-31 10:59:03 +0900447
Daniel Parkff178ba2018-11-23 15:57:24 +0900448 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
449
450 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
451 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
452 return UNSUPPORTED_VENDOR;
453 }
454
455 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
456
Daniel Parkc4d06402018-05-28 15:57:37 +0900457 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
458 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
459
460 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
461 .split(":")[2]
462 .split("\\.")[0];
463 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
464
465 String functionNumHex = port.getProfile().get(PCISLOT).toString()
466 .split(":")[2]
467 .split("\\.")[1];
468 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
469
Daniel Parkc4d06402018-05-28 15:57:37 +0900470 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
471 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
472 } else {
473 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
474 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
475 }
476
477 return intfName;
478 }
Jian Li51b844c2018-05-31 10:59:03 +0900479
480 /**
Daniel Park95f73312018-07-31 15:48:34 +0900481 * Check if the given interface is added to the given device or not.
482 *
483 * @param deviceId device ID
484 * @param intfName interface name
485 * @param deviceService device service
486 * @return true if the given interface is added to the given device or false otherwise
487 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900488 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
489 String intfName,
490 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900491 checkNotNull(deviceId);
492 checkNotNull(intfName);
493
Jian Li5ecfd1a2018-12-10 11:41:03 +0900494 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
495 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900496 }
497
498 /**
Jian Li0b564282018-06-20 00:50:53 +0900499 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900500 *
501 * @param osPort port
502 * @param adminService openstack admin service
503 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900504 public static void addRouterIface(Port osPort,
505 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900506 osPort.getFixedIps().forEach(p -> {
507 JsonNode jsonTree = new ObjectMapper().createObjectNode()
508 .put("id", osPort.getDeviceId())
509 .put("tenant_id", osPort.getTenantId())
510 .put("subnet_id", p.getSubnetId())
511 .put("port_id", osPort.getId());
512 try {
513 RouterInterface rIface = getContext(NeutronRouterInterface.class)
514 .readerFor(NeutronRouterInterface.class)
515 .readValue(jsonTree);
516 if (adminService.routerInterface(rIface.getPortId()) != null) {
517 adminService.updateRouterInterface(rIface);
518 } else {
519 adminService.addRouterInterface(rIface);
520 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900521 } catch (IOException e) {
522 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900523 }
524 });
525 }
526
527 /**
Jian Li40f032a2019-10-02 20:36:09 +0900528 * Prints openstack security group.
529 *
530 * @param osSg openstack security group
531 */
532 public static void printSecurityGroup(SecurityGroup osSg) {
Jian Li5b22f112020-10-05 22:36:41 +0900533 print(SECURITY_GROUP_FORMAT, osSg.getId(), deriveResourceName(osSg));
Jian Li40f032a2019-10-02 20:36:09 +0900534 }
535
536 /**
537 * Prints openstack network.
538 *
539 * @param osNet openstack network
540 */
541 public static void printNetwork(Network osNet) {
542 final String strNet = String.format(NETWORK_FORMAT,
543 osNet.getId(),
Jian Li5b22f112020-10-05 22:36:41 +0900544 deriveResourceName(osNet),
Jian Li40f032a2019-10-02 20:36:09 +0900545 osNet.getProviderSegID(),
546 osNet.getSubnets());
547 print(strNet);
548 }
549
550 /**
551 * Prints openstack subnet.
552 *
553 * @param osSubnet openstack subnet
554 * @param osNetService openstack network service
555 */
556 public static void printSubnet(Subnet osSubnet,
557 OpenstackNetworkService osNetService) {
558 final Network network = osNetService.network(osSubnet.getNetworkId());
Jian Li5b22f112020-10-05 22:36:41 +0900559 final String netName = network == null ? NOT_AVAILABLE : deriveResourceName(network);
Jian Li40f032a2019-10-02 20:36:09 +0900560 final String strSubnet = String.format(SUBNET_FORMAT,
561 osSubnet.getId(),
562 netName,
563 osSubnet.getCidr());
564 print(strSubnet);
565 }
566
567 /**
568 * Prints openstack port.
569 *
570 * @param osPort openstack port
571 * @param osNetService openstack network service
572 */
573 public static void printPort(Port osPort,
574 OpenstackNetworkService osNetService) {
575 List<String> fixedIps = osPort.getFixedIps().stream()
576 .map(IP::getIpAddress)
577 .collect(Collectors.toList());
578 final Network network = osNetService.network(osPort.getNetworkId());
Jian Li5b22f112020-10-05 22:36:41 +0900579 final String netName = network == null ? NOT_AVAILABLE : deriveResourceName(network);
Jian Li40f032a2019-10-02 20:36:09 +0900580 final String strPort = String.format(PORT_FORMAT,
581 osPort.getId(),
582 netName,
583 osPort.getMacAddress(),
584 fixedIps.isEmpty() ? "" : fixedIps);
585 print(strPort);
586 }
587
588 /**
589 * Prints openstack router.
590 *
591 * @param osRouter openstack router
592 * @param osNetService openstack network service
593 */
594 public static void printRouter(Router osRouter,
595 OpenstackNetworkService osNetService) {
596 List<String> externals = osNetService.ports().stream()
597 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
598 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_GW))
599 .flatMap(osPort -> osPort.getFixedIps().stream())
600 .map(IP::getIpAddress)
601 .collect(Collectors.toList());
602
603 List<String> internals = osNetService.ports().stream()
604 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
605 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
606 .flatMap(osPort -> osPort.getFixedIps().stream())
607 .map(IP::getIpAddress)
608 .collect(Collectors.toList());
609
610 final String strRouter = String.format(ROUTER_FORMAT,
611 osRouter.getId(),
Jian Li5b22f112020-10-05 22:36:41 +0900612 deriveResourceName(osRouter),
Jian Li40f032a2019-10-02 20:36:09 +0900613 externals.isEmpty() ? "" : externals,
614 internals.isEmpty() ? "" : internals);
615 print(strRouter);
616 }
617
618 /**
619 * Prints openstack router interface.
620 *
621 * @param osRouterIntf openstack router interface
622 */
623 public static void printRouterIntf(RouterInterface osRouterIntf) {
624 final String strRouterIntf = String.format(ROUTER_INTF_FORMAT,
625 osRouterIntf.getId(),
626 osRouterIntf.getTenantId(),
627 osRouterIntf.getSubnetId());
628 print(strRouterIntf);
629 }
630
631 /**
632 * Prints openstack floating IP.
633 *
634 * @param floatingIp floating IP
635 */
636 public static void printFloatingIp(NetFloatingIP floatingIp) {
637 final String strFloating = String.format(FLOATING_IP_FORMAT,
638 floatingIp.getId(),
639 floatingIp.getFloatingIpAddress(),
640 Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
641 "" : floatingIp.getFixedIpAddress());
642 print(strFloating);
643 }
644
645 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900646 * Obtains the property value with specified property key name.
647 *
648 * @param properties a collection of properties
649 * @param name key name
650 * @return mapping value
651 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900652 public static String getPropertyValue(Set<ConfigProperty> properties,
653 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900654 Optional<ConfigProperty> property =
655 properties.stream().filter(p -> p.name().equals(name)).findFirst();
656 return property.map(ConfigProperty::value).orElse(null);
657 }
658
659 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900660 * Obtains the boolean property value with specified property key name.
661 *
662 * @param properties a collection of properties
663 * @param name key name
664 * @return mapping value
665 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900666 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
667 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900668 Optional<ConfigProperty> property =
669 properties.stream().filter(p -> p.name().equals(name)).findFirst();
670
671 return property.map(ConfigProperty::asBoolean).orElse(false);
672 }
673
674 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900675 * Prints out the JSON string in pretty format.
676 *
677 * @param mapper Object mapper
678 * @param jsonString JSON string
679 * @return pretty formatted JSON string
680 */
681 public static String prettyJson(ObjectMapper mapper, String jsonString) {
682 try {
683 Object jsonObject = mapper.readValue(jsonString, Object.class);
684 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900685 } catch (JsonParseException e) {
686 log.debug("JsonParseException caused by {}", e);
687 } catch (JsonMappingException e) {
688 log.debug("JsonMappingException caused by {}", e);
689 } catch (JsonProcessingException e) {
690 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900691 }
692 return null;
693 }
694
695 /**
Jian Li7f024de2018-07-07 03:51:02 +0900696 * Checks the validity of ARP mode.
697 *
698 * @param arpMode ARP mode
699 * @return returns true if the ARP mode is valid, false otherwise
700 */
701 public static boolean checkArpMode(String arpMode) {
702
703 if (isNullOrEmpty(arpMode)) {
704 return false;
705 } else {
706 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
707 }
708 }
709
710 /**
Jian Licad36c72018-09-13 17:44:54 +0900711 * Checks the validity of activation flag.
712 *
713 * @param activationFlag activation flag
714 * @return returns true if the activation flag is valid, false otherwise
715 */
716 public static boolean checkActivationFlag(String activationFlag) {
717
718 switch (activationFlag) {
719 case ENABLE:
720 return true;
721 case DISABLE:
722 return false;
723 default:
724 throw new IllegalArgumentException("The given activation flag is not valid!");
725 }
726 }
727
728 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900729 * Swaps current location with old location info.
730 * The revised instance port will be used to mod the flow rules after migration.
731 *
732 * @param instPort instance port
733 * @return location swapped instance port
734 */
735 public static InstancePort swapStaleLocation(InstancePort instPort) {
736 return DefaultInstancePort.builder()
737 .deviceId(instPort.oldDeviceId())
738 .portNumber(instPort.oldPortNumber())
739 .state(instPort.state())
740 .ipAddress(instPort.ipAddress())
741 .macAddress(instPort.macAddress())
742 .networkId(instPort.networkId())
743 .portId(instPort.portId())
744 .build();
745 }
746
747 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900748 * Compares two router interfaces are equal.
749 * Will be remove this after Openstack4j implements equals.
750 *
751 * @param routerInterface1 router interface
752 * @param routerInterface2 router interface
753 * @return returns true if two router interfaces are equal, false otherwise
754 */
Jian Li63430202018-08-30 16:24:09 +0900755 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
756 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900757 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
758 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
759 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
760 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
761 }
762
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900763 /**
764 * Returns the vnic type of given port.
765 *
766 * @param portName port name
767 * @return vnit type
768 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900769 public static VnicType vnicType(String portName) {
770 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
771 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
772 return VnicType.NORMAL;
773 } else if (isDirectPort(portName)) {
774 return VnicType.DIRECT;
775 } else {
776 return VnicType.UNSUPPORTED;
777 }
778 }
779
Jian Li63430202018-08-30 16:24:09 +0900780 /**
781 * Deserializes raw payload into HttpRequest object.
782 *
783 * @param rawData raw http payload
784 * @return HttpRequest object
785 */
786 public static HttpRequest parseHttpRequest(byte[] rawData) {
787 SessionInputBufferImpl sessionInputBuffer =
788 new SessionInputBufferImpl(
789 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
790 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900791 DefaultHttpRequestParser requestParser =
792 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900793 try {
794 return requestParser.parse();
795 } catch (IOException | HttpException e) {
796 log.warn("Failed to parse HttpRequest, due to {}", e);
797 }
798
799 return null;
800 }
801
802 /**
803 * Serializes HttpRequest object to byte array.
804 *
805 * @param request http request object
806 * @return byte array
807 */
808 public static byte[] unparseHttpRequest(HttpRequest request) {
809 try {
810 SessionOutputBufferImpl sessionOutputBuffer =
811 new SessionOutputBufferImpl(
812 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
813
814 ByteArrayOutputStream baos = new ByteArrayOutputStream();
815 sessionOutputBuffer.bind(baos);
816
Jian Li5ecfd1a2018-12-10 11:41:03 +0900817 HttpMessageWriter<HttpRequest> requestWriter =
818 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900819 requestWriter.write(request);
820 sessionOutputBuffer.flush();
821
822 return baos.toByteArray();
823 } catch (HttpException | IOException e) {
824 log.warn("Failed to unparse HttpRequest, due to {}", e);
825 }
826
827 return null;
828 }
829
830 /**
831 * Deserializes raw payload into HttpResponse object.
832 *
833 * @param rawData raw http payload
834 * @return HttpResponse object
835 */
836 public static HttpResponse parseHttpResponse(byte[] rawData) {
837 SessionInputBufferImpl sessionInputBuffer =
838 new SessionInputBufferImpl(
839 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
840 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900841 DefaultHttpResponseParser responseParser =
842 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900843 try {
844 return responseParser.parse();
845 } catch (IOException | HttpException e) {
846 log.warn("Failed to parse HttpResponse, due to {}", e);
847 }
848
849 return null;
850 }
851
852 /**
853 * Serializes HttpResponse header to byte array.
854 *
855 * @param response http response object
856 * @return byte array
857 */
858 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
859 try {
860 SessionOutputBufferImpl sessionOutputBuffer =
861 new SessionOutputBufferImpl(
862 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
863
864 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
865 sessionOutputBuffer.bind(headerBaos);
866
867 HttpMessageWriter<HttpResponse> responseWriter =
868 new DefaultHttpResponseWriter(sessionOutputBuffer);
869 responseWriter.write(response);
870 sessionOutputBuffer.flush();
871
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900872 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900873
874 return headerBaos.toByteArray();
875 } catch (IOException | HttpException e) {
876 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
877 }
878
879 return null;
880 }
881
882 /**
883 * Serializes HttpResponse object to byte array.
884 *
885 * @param response http response object
886 * @return byte array
887 */
888 public static byte[] unparseHttpResponseBody(HttpResponse response) {
889 try {
890 ByteArrayOutputStream baos = new ByteArrayOutputStream();
891 response.getEntity().writeTo(baos);
892
893 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900894 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900895
896 return baos.toByteArray();
897 } catch (IOException e) {
898 log.warn("Failed to unparse HttpResponse, due to {}", e);
899 }
900
901 return null;
902 }
903
904 /**
905 * Encodes the given data using HmacSHA256 encryption method with given secret key.
906 *
907 * @param key secret key
908 * @param data data to be encrypted
909 * @return Hmac256 encrypted data
910 */
911 public static String hmacEncrypt(String key, String data) {
912 try {
913 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
914 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
915 sha256Hmac.init(secretKey);
916 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
917 } catch (Exception e) {
918 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
919 }
920 return null;
921 }
922
Daniel Parka73c2362018-09-17 17:43:25 +0900923 /**
924 * Creates flow trace request string.
925 *
926 * @param srcIp src ip address
927 * @param dstIp dst ip address
928 * @param srcInstancePort src instance port
929 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900930 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900931 * @return flow trace request string
932 */
933 public static String traceRequestString(String srcIp,
934 String dstIp,
935 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900936 OpenstackNetworkService osNetService,
937 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900938
939 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
940
941 if (uplink) {
942
943 requestStringBuilder.append(COMMA)
944 .append(IN_PORT)
945 .append(srcInstancePort.portNumber().toString())
946 .append(COMMA)
947 .append(NW_SRC)
948 .append(srcIp)
949 .append(COMMA);
950
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900951 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900952 Type netType = osNetService.networkType(srcInstancePort.networkId());
953 if (netType == Type.VXLAN || netType == Type.GRE ||
954 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900955 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900956 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900957 requestStringBuilder.append(DL_DST)
958 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900959 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
960 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900961 requestStringBuilder.append(DL_DST)
962 .append(DEFAULT_GATEWAY_MAC_STR)
963 .append(COMMA);
964 }
965 } else {
966 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900967 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900968 }
969 }
970
971 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900972 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900973 .append("\n");
974 } else {
975 requestStringBuilder.append(COMMA)
976 .append(NW_SRC)
977 .append(dstIp)
978 .append(COMMA);
979
Jian Li621f73c2018-12-15 01:49:22 +0900980 Type netType = osNetService.networkType(srcInstancePort.networkId());
981
982 if (netType == Type.VXLAN || netType == Type.GRE ||
983 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900984 requestStringBuilder.append(TUN_ID)
985 .append(osNetService.segmentId(srcInstancePort.networkId()))
986 .append(COMMA);
987 }
988 requestStringBuilder.append(NW_DST)
989 .append(srcIp)
990 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900991 }
992
993 return requestStringBuilder.toString();
994 }
995
996 /**
997 * Sends flow trace string to node.
998 *
999 * @param requestString reqeust string
1000 * @param node src node
1001 * @return flow trace result in string format
1002 */
1003 public static String sendTraceRequestToNode(String requestString,
1004 OpenstackNode node) {
1005 String traceResult = null;
1006 OpenstackSshAuth sshAuth = node.sshAuthInfo();
1007
1008 try (SshClient client = SshClient.setUpDefaultClient()) {
1009 client.start();
1010
1011 try (ClientSession session = client
1012 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
1013 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
1014 session.addPasswordIdentity(sshAuth.password());
1015 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
1016
1017
1018 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
1019
1020 log.debug("requestString: {}", requestString);
1021 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001022 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +09001023
Jian Li5ecfd1a2018-12-10 11:41:03 +09001024 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +09001025 OutputStream errStream = new ByteArrayOutputStream();
1026
1027 channel.setIn(new NoCloseInputStream(inputStream));
1028 channel.setErr(errStream);
1029 channel.setOut(outputStream);
1030
1031 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
1032 eventList.add(ClientChannelEvent.OPENED);
1033
1034 OpenFuture channelFuture = channel.open();
1035
1036 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
1037
1038 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
1039
1040 while (!channelFuture.isOpened()) {
1041 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
1042 log.error("Failed to open channel");
1043 return null;
1044 }
1045 }
1046 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
1047
Jian Li5ecfd1a2018-12-10 11:41:03 +09001048 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +09001049
1050 channel.close();
1051 }
1052 } finally {
1053 session.close();
1054 }
1055 } finally {
1056 client.stop();
1057 }
1058
1059 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001060 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +09001061 }
1062
1063 return traceResult;
1064 }
1065
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001066 /**
1067 * Returns the floating ip with supplied instance port.
1068 *
1069 * @param instancePort instance port
1070 * @param osRouterAdminService openstack router admin service
1071 * @return floating ip
1072 */
1073 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001074 OpenstackRouterAdminService
1075 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001076 return osRouterAdminService.floatingIps().stream()
1077 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
1078 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
1079 .findAny().orElse(null);
1080 }
1081
1082 /**
1083 * Sends GARP packet with supplied floating ip information.
1084 *
1085 * @param floatingIP floating ip
1086 * @param instancePort instance port
1087 * @param vlanId vlain id
1088 * @param gatewayNode gateway node
1089 * @param packetService packet service
1090 */
Jian Li32b03622018-11-06 17:54:24 +09001091 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
1092 InstancePort instancePort,
1093 VlanId vlanId,
1094 OpenstackNode gatewayNode,
1095 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001096 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
1097
1098 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1099 .setOutput(gatewayNode.uplinkPortNum()).build();
1100
1101 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
1102 ByteBuffer.wrap(ethernet.serialize())));
1103 }
1104
1105 /**
1106 * Returns the external peer router with supplied network information.
1107 *
1108 * @param network network
1109 * @param osNetworkService openstack network service
1110 * @param osRouterAdminService openstack router admin service
1111 * @return external peer router
1112 */
1113 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001114 OpenstackNetworkService
1115 osNetworkService,
1116 OpenstackRouterAdminService
1117 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001118 if (network == null) {
1119 return null;
1120 }
1121
Jian Lie6e609f2019-05-14 17:45:54 +09001122 Subnet subnet = osNetworkService.subnets(network.getId())
1123 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001124
1125 if (subnet == null) {
1126 return null;
1127 }
1128
1129 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
1130 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1131 .findAny().orElse(null);
1132 if (osRouterIface == null) {
1133 return null;
1134 }
1135
1136 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001137 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001138 return null;
1139 }
1140
1141 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
1142 return osNetworkService.externalPeerRouter(exGatewayInfo);
1143
1144 }
1145
Jian Liebde74d2018-11-14 00:18:57 +09001146 /**
1147 * Returns the external peer router with specified subnet information.
1148 *
1149 * @param subnet openstack subnet
1150 * @param osRouterService openstack router service
1151 * @param osNetworkService openstack network service
1152 * @return external peer router
1153 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001154 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
1155 OpenstackRouterService
1156 osRouterService,
1157 OpenstackNetworkService
1158 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001159 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
1160 if (osRouter == null) {
1161 return null;
1162 }
1163 if (osRouter.getExternalGatewayInfo() == null) {
1164 // this router does not have external connectivity
Jian Li5b22f112020-10-05 22:36:41 +09001165 log.trace("router({}) has no external gateway", deriveResourceName(osRouter));
Jian Liebde74d2018-11-14 00:18:57 +09001166 return null;
1167 }
1168
1169 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1170 }
1171
1172 /**
Jian Lifa62b372020-02-24 16:42:24 +09001173 * Returns the external gateway IP address with specified router information.
Jian Liebde74d2018-11-14 00:18:57 +09001174 *
1175 * @param router openstack router
1176 * @param osNetworkService openstack network service
Jian Lifa62b372020-02-24 16:42:24 +09001177 * @return external IP address
Jian Liebde74d2018-11-14 00:18:57 +09001178 */
Jian Lifa62b372020-02-24 16:42:24 +09001179 public static IpAddress externalGatewayIp(Router router,
1180 OpenstackNetworkService osNetworkService) {
1181 return externalGatewayIpBase(router, false, osNetworkService);
1182 }
Jian Liebde74d2018-11-14 00:18:57 +09001183
Jian Lifa62b372020-02-24 16:42:24 +09001184 /**
1185 * Returns the external gateway IP address (SNAT enabled) with specified router information.
1186 *
1187 * @param router openstack router
1188 * @param osNetworkService openstack network service
1189 * @return external IP address
1190 */
1191 public static IpAddress externalGatewayIpSnatEnabled(Router router,
1192 OpenstackNetworkService osNetworkService) {
1193 return externalGatewayIpBase(router, true, osNetworkService);
Jian Liebde74d2018-11-14 00:18:57 +09001194 }
1195
Jian Li2d68c192018-12-13 15:52:59 +09001196 /**
1197 * Returns the tunnel port number with specified net ID and openstack node.
1198 *
1199 * @param netId network ID
1200 * @param netService network service
1201 * @param osNode openstack node
1202 * @return tunnel port number
1203 */
1204 public static PortNumber tunnelPortNumByNetId(String netId,
1205 OpenstackNetworkService netService,
1206 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001207 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001208
1209 if (netType == null) {
1210 return null;
1211 }
1212
1213 return tunnelPortNumByNetType(netType, osNode);
1214 }
1215
1216 /**
1217 * Returns the tunnel port number with specified net type and openstack node.
1218 *
1219 * @param netType network type
1220 * @param osNode openstack node
1221 * @return tunnel port number
1222 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001223 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001224 switch (netType) {
1225 case VXLAN:
1226 return osNode.vxlanTunnelPortNum();
1227 case GRE:
1228 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001229 case GENEVE:
1230 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001231 default:
1232 return null;
1233 }
1234 }
1235
Jian Li7b8c3682019-05-12 13:57:15 +09001236 /**
1237 * Returns the REST URL of active node.
1238 *
1239 * @param haService openstack HA service
1240 * @return REST URL of active node
1241 */
1242 public static String getActiveUrl(OpenstackHaService haService) {
1243 return "http://" + haService.getActiveIp().toString() + ":" +
1244 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1245 }
1246
1247 /**
1248 * Returns the REST client instance with given resource path.
1249 *
1250 * @param haService openstack HA service
1251 * @param resourcePath resource path
1252 * @return REST client instance
1253 */
1254 public static WebTarget getActiveClient(OpenstackHaService haService,
1255 String resourcePath) {
1256 HttpAuthenticationFeature feature =
1257 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1258 Client client = ClientBuilder.newClient().register(feature);
1259 return client.target(getActiveUrl(haService)).path(resourcePath);
1260 }
1261
1262 /**
1263 * Returns the post response from the active node.
1264 *
1265 * @param haService openstack HA service
1266 * @param resourcePath resource path
1267 * @param input input
1268 * @return post response
1269 */
1270 public static Response syncPost(OpenstackHaService haService,
1271 String resourcePath,
1272 String input) {
1273
1274 log.debug("Sync POST request with {} on {}",
1275 haService.getActiveIp().toString(), resourcePath);
1276
1277 return getActiveClient(haService, resourcePath)
1278 .request(APPLICATION_JSON_TYPE)
1279 .post(Entity.json(input));
1280 }
1281
1282 /**
1283 * Returns the put response from the active node.
1284 *
1285 * @param haService openstack HA service
1286 * @param resourcePath resource path
1287 * @param id resource identifier
1288 * @param input input
1289 * @return put response
1290 */
1291 public static Response syncPut(OpenstackHaService haService,
1292 String resourcePath,
1293 String id, String input) {
1294 return syncPut(haService, resourcePath, null, id, input);
1295 }
1296
1297 /**
1298 * Returns the put response from the active node.
1299 *
1300 * @param haService openstack HA service
1301 * @param resourcePath resource path
1302 * @param id resource identifier
1303 * @param suffix resource suffix
1304 * @param input input
1305 * @return put response
1306 */
1307 public static Response syncPut(OpenstackHaService haService,
1308 String resourcePath,
1309 String suffix,
1310 String id, String input) {
1311
1312 log.debug("Sync PUT request with {} on {}",
1313 haService.getActiveIp().toString(), resourcePath);
1314
1315 String pathStr = "/" + id;
1316
1317 if (suffix != null) {
1318 pathStr += "/" + suffix;
1319 }
1320
1321 return getActiveClient(haService, resourcePath)
1322 .path(pathStr)
1323 .request(APPLICATION_JSON_TYPE)
1324 .put(Entity.json(input));
1325 }
1326
1327 /**
1328 * Returns the delete response from the active node.
1329 *
1330 * @param haService openstack HA service
1331 * @param resourcePath resource path
1332 * @param id resource identifier
1333 * @return delete response
1334 */
1335 public static Response syncDelete(OpenstackHaService haService,
1336 String resourcePath,
1337 String id) {
1338
1339 log.debug("Sync DELETE request with {} on {}",
1340 haService.getActiveIp().toString(), resourcePath);
1341
1342 return getActiveClient(haService, resourcePath)
1343 .path("/" + id)
1344 .request(APPLICATION_JSON_TYPE)
1345 .delete();
1346 }
1347
Jian Li51728702019-05-17 18:38:56 +09001348 /**
1349 * Gets the ovsdb client with supplied openstack node.
1350 *
1351 * @param node openstack node
1352 * @param ovsdbPort openvswitch DB port number
1353 * @param controller openvswitch DB controller instance
1354 * @return ovsdb client instance
1355 */
1356 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1357 OvsdbController controller) {
1358 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1359 return controller.getOvsdbClient(ovsdb);
1360 }
1361
1362 /**
1363 * Obtains the name of interface attached to the openstack VM.
1364 *
1365 * @param portId openstack port identifier
1366 * @return name of interface
1367 */
1368 public static String ifaceNameFromOsPortId(String portId) {
1369 if (portId != null) {
1370 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1371 }
1372
1373 return null;
1374 }
1375
Jian Lifa62b372020-02-24 16:42:24 +09001376 /**
1377 * Return the router associated with the given subnet.
1378 *
1379 * @param subnet openstack subnet
1380 * @param osRouterService openstack router service
1381 * @return router
1382 */
1383 public static Router getRouterFromSubnet(Subnet subnet,
1384 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001385 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1386 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1387 .findAny().orElse(null);
1388 if (osRouterIface == null) {
1389 return null;
1390 }
1391
1392 return osRouterService.router(osRouterIface.getId());
1393 }
1394
Daniel Park7e8c4d82018-08-13 23:47:49 +09001395 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001396 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001397 }
1398
Daniel Park2ff66b42018-08-01 11:52:45 +09001399 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001400 * Returns GARP packet with supplied floating ip and instance port information.
1401 *
1402 * @param floatingIP floating ip
1403 * @param instancePort instance port
1404 * @param vlanId vlan id
1405 * @return GARP packet
1406 */
1407 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1408 InstancePort instancePort,
1409 VlanId vlanId) {
1410 Ethernet ethernet = new Ethernet();
1411 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1412 ethernet.setSourceMACAddress(instancePort.macAddress());
1413 ethernet.setEtherType(Ethernet.TYPE_ARP);
1414 ethernet.setVlanID(vlanId.id());
1415
1416 ARP arp = new ARP();
1417 arp.setOpCode(ARP.OP_REPLY);
1418 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1419 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1420
1421 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1422 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1423
1424 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1425 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1426
Jian Li5ecfd1a2018-12-10 11:41:03 +09001427 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1428 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001429
1430 ethernet.setPayload(arp);
1431
1432 return ethernet;
1433 }
1434
1435 /**
Jian Lia271b3c2019-09-03 23:10:20 +09001436 * Re-structures the OVS port name.
1437 * The length of OVS port name should be not large than 15.
1438 *
1439 * @param portName original port name
1440 * @return re-structured OVS port name
1441 */
1442 public static String structurePortName(String portName) {
1443
1444 // The size of OVS port name should not be larger than 15
1445 if (portName.length() > PORT_NAME_MAX_LENGTH) {
1446 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
1447 }
1448
1449 return portName;
1450 }
1451
1452 /**
Jian Lid5727622019-09-11 11:15:16 +09001453 * Obtains flow group key from the given id.
1454 *
1455 * @param groupId flow group identifier
1456 * @return flow group key
1457 */
1458 public static GroupKey getGroupKey(int groupId) {
1459 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
1460 }
1461
1462 /**
Jian Li6a1bcfd2020-01-30 17:41:26 +09001463 * Calculate the broadcast address from given IP address and subnet prefix length.
1464 *
1465 * @param ipAddr IP address
1466 * @param prefixLength subnet prefix length
1467 * @return broadcast address
1468 */
1469 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
1470 String subnet = ipAddr + "/" + prefixLength;
1471 SubnetUtils utils = new SubnetUtils(subnet);
1472 return utils.getInfo().getBroadcastAddress();
1473 }
1474
1475 /**
Jian Li1951db72020-05-11 13:05:47 +09001476 * Obtains the DHCP server name from option.
1477 *
1478 * @param port neutron port
1479 * @return server name
1480 */
1481 public static String getDhcpServerName(NeutronPort port) {
1482 return getDhcpOptionValue(port, "server-ip-address");
1483 }
1484
1485 /**
1486 * Obtains the DHCP static boot file name from option.
1487 *
1488 * @param port neutron port
1489 * @return DHCP static boot file name
1490 */
1491 public static String getDhcpStaticBootFileName(NeutronPort port) {
1492 return getDhcpOptionValue(port, "tag:!ipxe,67");
1493 }
1494
1495 /**
1496 * Obtains the DHCP full boot file name from option.
1497 *
1498 * @param port neutron port
1499 * @return DHCP full boot file name
1500 */
1501 public static String getDhcpFullBootFileName(NeutronPort port) {
1502 return getDhcpOptionValue(port, "tag:ipxe,67");
1503 }
1504
Jian Li5b22f112020-10-05 22:36:41 +09001505 /**
1506 * Returns a valid resource name.
1507 *
1508 * @param resource openstack basic resource object
1509 * @return a valid resource name
1510 */
1511 public static String deriveResourceName(BasicResource resource) {
1512 if (Strings.isNullOrEmpty(resource.getName())) {
1513 return PARENTHESES_START + resource.getId() + PARENTHESES_END;
1514 } else {
1515 return resource.getName();
1516 }
1517 }
1518
Jian Li1951db72020-05-11 13:05:47 +09001519 private static String getDhcpOptionValue(NeutronPort port, String optionNameStr) {
1520 ObjectNode node = modelEntityToJson(port, NeutronPort.class);
1521
1522 if (node != null) {
1523 JsonNode portJson = node.get("port");
1524 ArrayNode options = (ArrayNode) portJson.get("extra_dhcp_opts");
1525 for (JsonNode option : options) {
1526 String optionName = option.get("optName").asText();
1527 if (StringUtils.equals(optionName, optionNameStr)) {
1528 return option.get("optValue").asText();
1529 }
1530 }
1531 }
1532
1533 return null;
1534 }
1535
1536 /**
Jian Li51b844c2018-05-31 10:59:03 +09001537 * Builds up and a complete endpoint URL from gateway node.
1538 *
1539 * @param node gateway node
1540 * @return a complete endpoint URL
1541 */
1542 private static String buildEndpoint(OpenstackNode node) {
1543
Jian Lic704b672018-09-04 18:52:53 +09001544 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001545
1546 StringBuilder endpointSb = new StringBuilder();
1547 endpointSb.append(auth.protocol().name().toLowerCase());
1548 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001549 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001550 return endpointSb.toString();
1551 }
1552
1553 /**
1554 * Obtains the SSL config without verifying the certification.
1555 *
1556 * @return SSL config
1557 */
1558 private static Config getSslConfig() {
1559 // we bypass the SSL certification verification for now
1560 // TODO: verify server side SSL using a given certification
1561 Config config = Config.newConfig().withSSLVerificationDisabled();
1562
1563 TrustManager[] trustAllCerts = new TrustManager[]{
1564 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001565 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001566 public X509Certificate[] getAcceptedIssuers() {
1567 return null;
1568 }
1569
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001570 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001571 public void checkClientTrusted(X509Certificate[] certs,
1572 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001573 return;
Jian Li51b844c2018-05-31 10:59:03 +09001574 }
1575
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001576 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001577 public void checkServerTrusted(X509Certificate[] certs,
1578 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001579 return;
Jian Li51b844c2018-05-31 10:59:03 +09001580 }
1581 }
1582 };
1583
1584 HostnameVerifier allHostsValid = (hostname, session) -> true;
1585
1586 try {
1587 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1588 sc.init(null, trustAllCerts,
1589 new java.security.SecureRandom());
1590 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1591 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1592
1593 config.withSSLContext(sc);
1594 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001595 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001596 return null;
1597 }
1598
1599 return config;
1600 }
1601
1602 /**
1603 * Obtains the facing object with given openstack perspective.
1604 *
1605 * @param perspective keystone perspective
1606 * @return facing object
1607 */
1608 private static Facing getFacing(Perspective perspective) {
1609
1610 switch (perspective) {
1611 case PUBLIC:
1612 return Facing.PUBLIC;
1613 case ADMIN:
1614 return Facing.ADMIN;
1615 case INTERNAL:
1616 return Facing.INTERNAL;
1617 default:
1618 return null;
1619 }
1620 }
1621
1622 /**
1623 * Obtains gateway instance by giving index number.
1624 *
1625 * @param gws a collection of gateway nodes
1626 * @param index index number
1627 * @return gateway instance
1628 */
1629 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1630 Map<String, OpenstackNode> hashMap = new HashMap<>();
1631 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1632 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1633 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1634
1635 int intIndex = 0;
1636 OpenstackNode gw = null;
1637 while (iteratorKey.hasNext()) {
1638 String key = iteratorKey.next();
1639
1640 if (intIndex == index) {
1641 gw = treeMap.get(key);
1642 }
1643 intIndex++;
1644 }
1645 return gw;
1646 }
Jian Li40f032a2019-10-02 20:36:09 +09001647
Jian Lifa62b372020-02-24 16:42:24 +09001648 /**
1649 * Returns the external gateway IP address with specified router information.
1650 *
1651 * @param router openstack router
1652 * @param snatOnly true for only query SNAT enabled case, false otherwise
1653 * @param osNetworkService openstack network service
1654 * @return external IP address
1655 */
1656 private static IpAddress externalGatewayIpBase(Router router, boolean snatOnly,
1657 OpenstackNetworkService osNetworkService) {
1658 if (router == null) {
1659 return null;
1660 }
1661
1662 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1663 if (externalGateway == null) {
1664 log.info("Failed to get external IP for router {} because no " +
1665 "external gateway is associated with the router",
1666 router.getId());
1667 return null;
1668 }
1669
1670 if (snatOnly) {
1671 if (!externalGateway.isEnableSnat()) {
1672 log.warn("The given router {} SNAT is configured as false", router.getId());
1673 return null;
1674 }
1675 }
1676
1677 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1678 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1679 .stream()
1680 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1681 .findAny().orElse(null);
1682
1683 if (exGatewayPort == null) {
1684 return null;
1685 }
1686
1687 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1688 .findAny().get().getIpAddress());
1689 }
1690
Jian Li40f032a2019-10-02 20:36:09 +09001691 private static void print(String format, Object... args) {
1692 System.out.println(String.format(format, args));
1693 }
Jian Li63430202018-08-30 16:24:09 +09001694}