blob: 6df6aac145c36d0e5a9656e19afef853b4d2d5c2 [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 Lic55a74f2020-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 Lif654dd12020-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 Lie87c2712019-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 Li59e53442020-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 Liecbf10c2019-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 Liecbf10c2019-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 Lic55a74f2020-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 Liecbf10c2019-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 Liecbf10c2019-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 Lia97cec42019-10-31 22:24:17 +0900144import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
Jian Liecbf10c2019-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 Liecbf10c2019-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 Liecbf10c2019-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 Li59e53442020-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 Liecbf10c2019-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 Li62116942019-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 Lia97cec42019-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 Liecbf10c2019-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 Li59e53442020-10-05 22:36:41 +0900533 print(SECURITY_GROUP_FORMAT, osSg.getId(), deriveResourceName(osSg));
Jian Liecbf10c2019-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 Li59e53442020-10-05 22:36:41 +0900544 deriveResourceName(osNet),
Jian Liecbf10c2019-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 Li59e53442020-10-05 22:36:41 +0900559 final String netName = network == null ? NOT_AVAILABLE : deriveResourceName(network);
Jian Liecbf10c2019-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 Li59e53442020-10-05 22:36:41 +0900579 final String netName = network == null ? NOT_AVAILABLE : deriveResourceName(network);
Jian Liecbf10c2019-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 Li59e53442020-10-05 22:36:41 +0900612 deriveResourceName(osRouter),
Jian Liecbf10c2019-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 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900692 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900693 }
694 return null;
695 }
696
697 /**
Jian Li7f024de2018-07-07 03:51:02 +0900698 * Checks the validity of ARP mode.
699 *
700 * @param arpMode ARP mode
701 * @return returns true if the ARP mode is valid, false otherwise
702 */
703 public static boolean checkArpMode(String arpMode) {
704
705 if (isNullOrEmpty(arpMode)) {
706 return false;
707 } else {
708 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
709 }
710 }
711
712 /**
Jian Licad36c72018-09-13 17:44:54 +0900713 * Checks the validity of activation flag.
714 *
715 * @param activationFlag activation flag
716 * @return returns true if the activation flag is valid, false otherwise
717 */
718 public static boolean checkActivationFlag(String activationFlag) {
719
720 switch (activationFlag) {
721 case ENABLE:
722 return true;
723 case DISABLE:
724 return false;
725 default:
726 throw new IllegalArgumentException("The given activation flag is not valid!");
727 }
728 }
729
730 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900731 * Swaps current location with old location info.
732 * The revised instance port will be used to mod the flow rules after migration.
733 *
734 * @param instPort instance port
735 * @return location swapped instance port
736 */
737 public static InstancePort swapStaleLocation(InstancePort instPort) {
738 return DefaultInstancePort.builder()
739 .deviceId(instPort.oldDeviceId())
740 .portNumber(instPort.oldPortNumber())
741 .state(instPort.state())
742 .ipAddress(instPort.ipAddress())
743 .macAddress(instPort.macAddress())
744 .networkId(instPort.networkId())
745 .portId(instPort.portId())
746 .build();
747 }
748
749 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900750 * Compares two router interfaces are equal.
751 * Will be remove this after Openstack4j implements equals.
752 *
753 * @param routerInterface1 router interface
754 * @param routerInterface2 router interface
755 * @return returns true if two router interfaces are equal, false otherwise
756 */
Jian Li63430202018-08-30 16:24:09 +0900757 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
758 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900759 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
760 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
761 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
762 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
763 }
764
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900765 /**
766 * Returns the vnic type of given port.
767 *
768 * @param portName port name
769 * @return vnit type
770 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900771 public static VnicType vnicType(String portName) {
772 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
773 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
774 return VnicType.NORMAL;
775 } else if (isDirectPort(portName)) {
776 return VnicType.DIRECT;
777 } else {
778 return VnicType.UNSUPPORTED;
779 }
780 }
781
Jian Li63430202018-08-30 16:24:09 +0900782 /**
783 * Deserializes raw payload into HttpRequest object.
784 *
785 * @param rawData raw http payload
786 * @return HttpRequest object
787 */
788 public static HttpRequest parseHttpRequest(byte[] rawData) {
789 SessionInputBufferImpl sessionInputBuffer =
790 new SessionInputBufferImpl(
791 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
792 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900793 DefaultHttpRequestParser requestParser =
794 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900795 try {
796 return requestParser.parse();
797 } catch (IOException | HttpException e) {
798 log.warn("Failed to parse HttpRequest, due to {}", e);
799 }
800
801 return null;
802 }
803
804 /**
805 * Serializes HttpRequest object to byte array.
806 *
807 * @param request http request object
808 * @return byte array
809 */
810 public static byte[] unparseHttpRequest(HttpRequest request) {
811 try {
812 SessionOutputBufferImpl sessionOutputBuffer =
813 new SessionOutputBufferImpl(
814 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
815
816 ByteArrayOutputStream baos = new ByteArrayOutputStream();
817 sessionOutputBuffer.bind(baos);
818
Jian Li5ecfd1a2018-12-10 11:41:03 +0900819 HttpMessageWriter<HttpRequest> requestWriter =
820 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900821 requestWriter.write(request);
822 sessionOutputBuffer.flush();
823
824 return baos.toByteArray();
825 } catch (HttpException | IOException e) {
826 log.warn("Failed to unparse HttpRequest, due to {}", e);
827 }
828
829 return null;
830 }
831
832 /**
833 * Deserializes raw payload into HttpResponse object.
834 *
835 * @param rawData raw http payload
836 * @return HttpResponse object
837 */
838 public static HttpResponse parseHttpResponse(byte[] rawData) {
839 SessionInputBufferImpl sessionInputBuffer =
840 new SessionInputBufferImpl(
841 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
842 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900843 DefaultHttpResponseParser responseParser =
844 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900845 try {
846 return responseParser.parse();
847 } catch (IOException | HttpException e) {
848 log.warn("Failed to parse HttpResponse, due to {}", e);
849 }
850
851 return null;
852 }
853
854 /**
855 * Serializes HttpResponse header to byte array.
856 *
857 * @param response http response object
858 * @return byte array
859 */
860 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
861 try {
862 SessionOutputBufferImpl sessionOutputBuffer =
863 new SessionOutputBufferImpl(
864 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
865
866 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
867 sessionOutputBuffer.bind(headerBaos);
868
869 HttpMessageWriter<HttpResponse> responseWriter =
870 new DefaultHttpResponseWriter(sessionOutputBuffer);
871 responseWriter.write(response);
872 sessionOutputBuffer.flush();
873
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900874 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900875
876 return headerBaos.toByteArray();
877 } catch (IOException | HttpException e) {
878 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
879 }
880
881 return null;
882 }
883
884 /**
885 * Serializes HttpResponse object to byte array.
886 *
887 * @param response http response object
888 * @return byte array
889 */
890 public static byte[] unparseHttpResponseBody(HttpResponse response) {
891 try {
892 ByteArrayOutputStream baos = new ByteArrayOutputStream();
893 response.getEntity().writeTo(baos);
894
895 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900896 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900897
898 return baos.toByteArray();
899 } catch (IOException e) {
900 log.warn("Failed to unparse HttpResponse, due to {}", e);
901 }
902
903 return null;
904 }
905
906 /**
907 * Encodes the given data using HmacSHA256 encryption method with given secret key.
908 *
909 * @param key secret key
910 * @param data data to be encrypted
911 * @return Hmac256 encrypted data
912 */
913 public static String hmacEncrypt(String key, String data) {
914 try {
915 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
916 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
917 sha256Hmac.init(secretKey);
918 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
919 } catch (Exception e) {
920 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
921 }
922 return null;
923 }
924
Daniel Parka73c2362018-09-17 17:43:25 +0900925 /**
926 * Creates flow trace request string.
927 *
928 * @param srcIp src ip address
929 * @param dstIp dst ip address
930 * @param srcInstancePort src instance port
931 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900932 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900933 * @return flow trace request string
934 */
935 public static String traceRequestString(String srcIp,
936 String dstIp,
937 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900938 OpenstackNetworkService osNetService,
939 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900940
941 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
942
943 if (uplink) {
944
945 requestStringBuilder.append(COMMA)
946 .append(IN_PORT)
947 .append(srcInstancePort.portNumber().toString())
948 .append(COMMA)
949 .append(NW_SRC)
950 .append(srcIp)
951 .append(COMMA);
952
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900953 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900954 Type netType = osNetService.networkType(srcInstancePort.networkId());
955 if (netType == Type.VXLAN || netType == Type.GRE ||
956 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900957 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900958 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900959 requestStringBuilder.append(DL_DST)
960 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900961 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
962 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900963 requestStringBuilder.append(DL_DST)
964 .append(DEFAULT_GATEWAY_MAC_STR)
965 .append(COMMA);
966 }
967 } else {
968 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900969 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900970 }
971 }
972
973 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900974 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900975 .append("\n");
976 } else {
977 requestStringBuilder.append(COMMA)
978 .append(NW_SRC)
979 .append(dstIp)
980 .append(COMMA);
981
Jian Li621f73c2018-12-15 01:49:22 +0900982 Type netType = osNetService.networkType(srcInstancePort.networkId());
983
984 if (netType == Type.VXLAN || netType == Type.GRE ||
985 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900986 requestStringBuilder.append(TUN_ID)
987 .append(osNetService.segmentId(srcInstancePort.networkId()))
988 .append(COMMA);
989 }
990 requestStringBuilder.append(NW_DST)
991 .append(srcIp)
992 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900993 }
994
995 return requestStringBuilder.toString();
996 }
997
998 /**
999 * Sends flow trace string to node.
1000 *
1001 * @param requestString reqeust string
1002 * @param node src node
1003 * @return flow trace result in string format
1004 */
1005 public static String sendTraceRequestToNode(String requestString,
1006 OpenstackNode node) {
1007 String traceResult = null;
1008 OpenstackSshAuth sshAuth = node.sshAuthInfo();
1009
1010 try (SshClient client = SshClient.setUpDefaultClient()) {
1011 client.start();
1012
1013 try (ClientSession session = client
1014 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
1015 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
1016 session.addPasswordIdentity(sshAuth.password());
1017 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
1018
1019
1020 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
1021
1022 log.debug("requestString: {}", requestString);
1023 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001024 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +09001025
Jian Li5ecfd1a2018-12-10 11:41:03 +09001026 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +09001027 OutputStream errStream = new ByteArrayOutputStream();
1028
1029 channel.setIn(new NoCloseInputStream(inputStream));
1030 channel.setErr(errStream);
1031 channel.setOut(outputStream);
1032
1033 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
1034 eventList.add(ClientChannelEvent.OPENED);
1035
1036 OpenFuture channelFuture = channel.open();
1037
1038 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
1039
1040 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
1041
1042 while (!channelFuture.isOpened()) {
1043 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
1044 log.error("Failed to open channel");
1045 return null;
1046 }
1047 }
1048 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
1049
Jian Li5ecfd1a2018-12-10 11:41:03 +09001050 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +09001051
1052 channel.close();
1053 }
1054 } finally {
1055 session.close();
1056 }
1057 } finally {
1058 client.stop();
1059 }
1060
1061 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001062 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +09001063 }
1064
1065 return traceResult;
1066 }
1067
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001068 /**
1069 * Returns the floating ip with supplied instance port.
1070 *
1071 * @param instancePort instance port
1072 * @param osRouterAdminService openstack router admin service
1073 * @return floating ip
1074 */
1075 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001076 OpenstackRouterAdminService
1077 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001078 return osRouterAdminService.floatingIps().stream()
1079 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
1080 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
1081 .findAny().orElse(null);
1082 }
1083
1084 /**
1085 * Sends GARP packet with supplied floating ip information.
1086 *
1087 * @param floatingIP floating ip
1088 * @param instancePort instance port
1089 * @param vlanId vlain id
1090 * @param gatewayNode gateway node
1091 * @param packetService packet service
1092 */
Jian Li32b03622018-11-06 17:54:24 +09001093 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
1094 InstancePort instancePort,
1095 VlanId vlanId,
1096 OpenstackNode gatewayNode,
1097 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001098 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
1099
1100 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1101 .setOutput(gatewayNode.uplinkPortNum()).build();
1102
1103 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
1104 ByteBuffer.wrap(ethernet.serialize())));
1105 }
1106
1107 /**
1108 * Returns the external peer router with supplied network information.
1109 *
1110 * @param network network
1111 * @param osNetworkService openstack network service
1112 * @param osRouterAdminService openstack router admin service
1113 * @return external peer router
1114 */
1115 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001116 OpenstackNetworkService
1117 osNetworkService,
1118 OpenstackRouterAdminService
1119 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001120 if (network == null) {
1121 return null;
1122 }
1123
Jian Lie6e609f2019-05-14 17:45:54 +09001124 Subnet subnet = osNetworkService.subnets(network.getId())
1125 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001126
1127 if (subnet == null) {
1128 return null;
1129 }
1130
1131 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
1132 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1133 .findAny().orElse(null);
1134 if (osRouterIface == null) {
1135 return null;
1136 }
1137
1138 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001139 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001140 return null;
1141 }
1142
1143 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
1144 return osNetworkService.externalPeerRouter(exGatewayInfo);
1145
1146 }
1147
Jian Liebde74d2018-11-14 00:18:57 +09001148 /**
1149 * Returns the external peer router with specified subnet information.
1150 *
1151 * @param subnet openstack subnet
1152 * @param osRouterService openstack router service
1153 * @param osNetworkService openstack network service
1154 * @return external peer router
1155 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001156 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
1157 OpenstackRouterService
1158 osRouterService,
1159 OpenstackNetworkService
1160 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001161 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
1162 if (osRouter == null) {
1163 return null;
1164 }
1165 if (osRouter.getExternalGatewayInfo() == null) {
1166 // this router does not have external connectivity
Jian Li59e53442020-10-05 22:36:41 +09001167 log.trace("router({}) has no external gateway", deriveResourceName(osRouter));
Jian Liebde74d2018-11-14 00:18:57 +09001168 return null;
1169 }
1170
1171 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1172 }
1173
1174 /**
Jian Lia5c7edf2020-02-24 16:42:24 +09001175 * Returns the external gateway IP address with specified router information.
Jian Liebde74d2018-11-14 00:18:57 +09001176 *
1177 * @param router openstack router
1178 * @param osNetworkService openstack network service
Jian Lia5c7edf2020-02-24 16:42:24 +09001179 * @return external IP address
Jian Liebde74d2018-11-14 00:18:57 +09001180 */
Jian Lia5c7edf2020-02-24 16:42:24 +09001181 public static IpAddress externalGatewayIp(Router router,
1182 OpenstackNetworkService osNetworkService) {
1183 return externalGatewayIpBase(router, false, osNetworkService);
1184 }
Jian Liebde74d2018-11-14 00:18:57 +09001185
Jian Lia5c7edf2020-02-24 16:42:24 +09001186 /**
1187 * Returns the external gateway IP address (SNAT enabled) with specified router information.
1188 *
1189 * @param router openstack router
1190 * @param osNetworkService openstack network service
1191 * @return external IP address
1192 */
1193 public static IpAddress externalGatewayIpSnatEnabled(Router router,
1194 OpenstackNetworkService osNetworkService) {
1195 return externalGatewayIpBase(router, true, osNetworkService);
Jian Liebde74d2018-11-14 00:18:57 +09001196 }
1197
Jian Li2d68c192018-12-13 15:52:59 +09001198 /**
1199 * Returns the tunnel port number with specified net ID and openstack node.
1200 *
1201 * @param netId network ID
1202 * @param netService network service
1203 * @param osNode openstack node
1204 * @return tunnel port number
1205 */
1206 public static PortNumber tunnelPortNumByNetId(String netId,
1207 OpenstackNetworkService netService,
1208 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001209 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001210
1211 if (netType == null) {
1212 return null;
1213 }
1214
1215 return tunnelPortNumByNetType(netType, osNode);
1216 }
1217
1218 /**
1219 * Returns the tunnel port number with specified net type and openstack node.
1220 *
1221 * @param netType network type
1222 * @param osNode openstack node
1223 * @return tunnel port number
1224 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001225 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001226 switch (netType) {
1227 case VXLAN:
1228 return osNode.vxlanTunnelPortNum();
1229 case GRE:
1230 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001231 case GENEVE:
1232 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001233 default:
1234 return null;
1235 }
1236 }
1237
Jian Li7b8c3682019-05-12 13:57:15 +09001238 /**
1239 * Returns the REST URL of active node.
1240 *
1241 * @param haService openstack HA service
1242 * @return REST URL of active node
1243 */
1244 public static String getActiveUrl(OpenstackHaService haService) {
1245 return "http://" + haService.getActiveIp().toString() + ":" +
1246 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1247 }
1248
1249 /**
1250 * Returns the REST client instance with given resource path.
1251 *
1252 * @param haService openstack HA service
1253 * @param resourcePath resource path
1254 * @return REST client instance
1255 */
1256 public static WebTarget getActiveClient(OpenstackHaService haService,
1257 String resourcePath) {
1258 HttpAuthenticationFeature feature =
1259 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1260 Client client = ClientBuilder.newClient().register(feature);
1261 return client.target(getActiveUrl(haService)).path(resourcePath);
1262 }
1263
1264 /**
1265 * Returns the post response from the active node.
1266 *
1267 * @param haService openstack HA service
1268 * @param resourcePath resource path
1269 * @param input input
1270 * @return post response
1271 */
1272 public static Response syncPost(OpenstackHaService haService,
1273 String resourcePath,
1274 String input) {
1275
1276 log.debug("Sync POST request with {} on {}",
1277 haService.getActiveIp().toString(), resourcePath);
1278
1279 return getActiveClient(haService, resourcePath)
1280 .request(APPLICATION_JSON_TYPE)
1281 .post(Entity.json(input));
1282 }
1283
1284 /**
1285 * Returns the put response from the active node.
1286 *
1287 * @param haService openstack HA service
1288 * @param resourcePath resource path
1289 * @param id resource identifier
1290 * @param input input
1291 * @return put response
1292 */
1293 public static Response syncPut(OpenstackHaService haService,
1294 String resourcePath,
1295 String id, String input) {
1296 return syncPut(haService, resourcePath, null, id, input);
1297 }
1298
1299 /**
1300 * Returns the put response from the active node.
1301 *
1302 * @param haService openstack HA service
1303 * @param resourcePath resource path
1304 * @param id resource identifier
1305 * @param suffix resource suffix
1306 * @param input input
1307 * @return put response
1308 */
1309 public static Response syncPut(OpenstackHaService haService,
1310 String resourcePath,
1311 String suffix,
1312 String id, String input) {
1313
1314 log.debug("Sync PUT request with {} on {}",
1315 haService.getActiveIp().toString(), resourcePath);
1316
1317 String pathStr = "/" + id;
1318
1319 if (suffix != null) {
1320 pathStr += "/" + suffix;
1321 }
1322
1323 return getActiveClient(haService, resourcePath)
1324 .path(pathStr)
1325 .request(APPLICATION_JSON_TYPE)
1326 .put(Entity.json(input));
1327 }
1328
1329 /**
1330 * Returns the delete response from the active node.
1331 *
1332 * @param haService openstack HA service
1333 * @param resourcePath resource path
1334 * @param id resource identifier
1335 * @return delete response
1336 */
1337 public static Response syncDelete(OpenstackHaService haService,
1338 String resourcePath,
1339 String id) {
1340
1341 log.debug("Sync DELETE request with {} on {}",
1342 haService.getActiveIp().toString(), resourcePath);
1343
1344 return getActiveClient(haService, resourcePath)
1345 .path("/" + id)
1346 .request(APPLICATION_JSON_TYPE)
1347 .delete();
1348 }
1349
Jian Li51728702019-05-17 18:38:56 +09001350 /**
1351 * Gets the ovsdb client with supplied openstack node.
1352 *
1353 * @param node openstack node
1354 * @param ovsdbPort openvswitch DB port number
1355 * @param controller openvswitch DB controller instance
1356 * @return ovsdb client instance
1357 */
1358 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1359 OvsdbController controller) {
1360 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1361 return controller.getOvsdbClient(ovsdb);
1362 }
1363
1364 /**
1365 * Obtains the name of interface attached to the openstack VM.
1366 *
1367 * @param portId openstack port identifier
1368 * @return name of interface
1369 */
1370 public static String ifaceNameFromOsPortId(String portId) {
1371 if (portId != null) {
1372 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1373 }
1374
1375 return null;
1376 }
1377
Jian Lia5c7edf2020-02-24 16:42:24 +09001378 /**
1379 * Return the router associated with the given subnet.
1380 *
1381 * @param subnet openstack subnet
1382 * @param osRouterService openstack router service
1383 * @return router
1384 */
1385 public static Router getRouterFromSubnet(Subnet subnet,
1386 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001387 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1388 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1389 .findAny().orElse(null);
1390 if (osRouterIface == null) {
1391 return null;
1392 }
1393
1394 return osRouterService.router(osRouterIface.getId());
1395 }
1396
Daniel Park7e8c4d82018-08-13 23:47:49 +09001397 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001398 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001399 }
1400
Daniel Park2ff66b42018-08-01 11:52:45 +09001401 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001402 * Returns GARP packet with supplied floating ip and instance port information.
1403 *
1404 * @param floatingIP floating ip
1405 * @param instancePort instance port
1406 * @param vlanId vlan id
1407 * @return GARP packet
1408 */
1409 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1410 InstancePort instancePort,
1411 VlanId vlanId) {
1412 Ethernet ethernet = new Ethernet();
1413 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1414 ethernet.setSourceMACAddress(instancePort.macAddress());
1415 ethernet.setEtherType(Ethernet.TYPE_ARP);
1416 ethernet.setVlanID(vlanId.id());
1417
1418 ARP arp = new ARP();
1419 arp.setOpCode(ARP.OP_REPLY);
1420 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1421 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1422
1423 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1424 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1425
1426 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1427 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1428
Jian Li5ecfd1a2018-12-10 11:41:03 +09001429 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1430 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001431
1432 ethernet.setPayload(arp);
1433
1434 return ethernet;
1435 }
1436
1437 /**
Jian Li62116942019-09-03 23:10:20 +09001438 * Re-structures the OVS port name.
1439 * The length of OVS port name should be not large than 15.
1440 *
1441 * @param portName original port name
1442 * @return re-structured OVS port name
1443 */
1444 public static String structurePortName(String portName) {
1445
1446 // The size of OVS port name should not be larger than 15
1447 if (portName.length() > PORT_NAME_MAX_LENGTH) {
1448 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
1449 }
1450
1451 return portName;
1452 }
1453
1454 /**
Jian Lie87c2712019-09-11 11:15:16 +09001455 * Obtains flow group key from the given id.
1456 *
1457 * @param groupId flow group identifier
1458 * @return flow group key
1459 */
1460 public static GroupKey getGroupKey(int groupId) {
1461 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
1462 }
1463
1464 /**
Jian Lif654dd12020-01-30 17:41:26 +09001465 * Calculate the broadcast address from given IP address and subnet prefix length.
1466 *
1467 * @param ipAddr IP address
1468 * @param prefixLength subnet prefix length
1469 * @return broadcast address
1470 */
1471 public static String getBroadcastAddr(String ipAddr, int prefixLength) {
1472 String subnet = ipAddr + "/" + prefixLength;
1473 SubnetUtils utils = new SubnetUtils(subnet);
1474 return utils.getInfo().getBroadcastAddress();
1475 }
1476
1477 /**
Jian Lic55a74f2020-05-11 13:05:47 +09001478 * Obtains the DHCP server name from option.
1479 *
1480 * @param port neutron port
1481 * @return server name
1482 */
1483 public static String getDhcpServerName(NeutronPort port) {
1484 return getDhcpOptionValue(port, "server-ip-address");
1485 }
1486
1487 /**
1488 * Obtains the DHCP static boot file name from option.
1489 *
1490 * @param port neutron port
1491 * @return DHCP static boot file name
1492 */
1493 public static String getDhcpStaticBootFileName(NeutronPort port) {
1494 return getDhcpOptionValue(port, "tag:!ipxe,67");
1495 }
1496
1497 /**
1498 * Obtains the DHCP full boot file name from option.
1499 *
1500 * @param port neutron port
1501 * @return DHCP full boot file name
1502 */
1503 public static String getDhcpFullBootFileName(NeutronPort port) {
1504 return getDhcpOptionValue(port, "tag:ipxe,67");
1505 }
1506
Jian Li59e53442020-10-05 22:36:41 +09001507 /**
1508 * Returns a valid resource name.
1509 *
1510 * @param resource openstack basic resource object
1511 * @return a valid resource name
1512 */
1513 public static String deriveResourceName(BasicResource resource) {
1514 if (Strings.isNullOrEmpty(resource.getName())) {
1515 return PARENTHESES_START + resource.getId() + PARENTHESES_END;
1516 } else {
1517 return resource.getName();
1518 }
1519 }
1520
Jian Li4b5048a2020-10-08 02:57:45 +09001521 /**
1522 * Returns a shifted IP address.
1523 *
1524 * @param ipAddress IP address to be shifted
1525 * @param shiftPrefix A IP prefix used in shifted IP address
1526 * @return shifted Ip address
1527 */
1528 public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
1529 String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
1530 return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
1531 }
1532
1533 /**
1534 * Returns an unshifted IP address.
1535 *
1536 * @param ipAddress IP address to be unshifted
1537 * @param ipPrefix IP prefix which to be used for unshifting
1538 * @param cidr a POD network CIDR
1539 * @return unshifted IP address
1540 */
1541 public static String unshiftIpDomain(String ipAddress,
1542 String ipPrefix,
1543 String cidr) {
1544
1545 String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
1546 return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
1547 }
1548
Jian Lic55a74f2020-05-11 13:05:47 +09001549 private static String getDhcpOptionValue(NeutronPort port, String optionNameStr) {
1550 ObjectNode node = modelEntityToJson(port, NeutronPort.class);
1551
1552 if (node != null) {
1553 JsonNode portJson = node.get("port");
1554 ArrayNode options = (ArrayNode) portJson.get("extra_dhcp_opts");
1555 for (JsonNode option : options) {
1556 String optionName = option.get("optName").asText();
1557 if (StringUtils.equals(optionName, optionNameStr)) {
1558 return option.get("optValue").asText();
1559 }
1560 }
1561 }
1562
1563 return null;
1564 }
1565
1566 /**
Jian Li51b844c2018-05-31 10:59:03 +09001567 * Builds up and a complete endpoint URL from gateway node.
1568 *
1569 * @param node gateway node
1570 * @return a complete endpoint URL
1571 */
1572 private static String buildEndpoint(OpenstackNode node) {
1573
Jian Lic704b672018-09-04 18:52:53 +09001574 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001575
1576 StringBuilder endpointSb = new StringBuilder();
1577 endpointSb.append(auth.protocol().name().toLowerCase());
1578 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001579 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001580 return endpointSb.toString();
1581 }
1582
1583 /**
1584 * Obtains the SSL config without verifying the certification.
1585 *
1586 * @return SSL config
1587 */
1588 private static Config getSslConfig() {
1589 // we bypass the SSL certification verification for now
1590 // TODO: verify server side SSL using a given certification
1591 Config config = Config.newConfig().withSSLVerificationDisabled();
1592
1593 TrustManager[] trustAllCerts = new TrustManager[]{
1594 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001595 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001596 public X509Certificate[] getAcceptedIssuers() {
1597 return null;
1598 }
1599
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001600 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001601 public void checkClientTrusted(X509Certificate[] certs,
1602 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001603 return;
Jian Li51b844c2018-05-31 10:59:03 +09001604 }
1605
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001606 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001607 public void checkServerTrusted(X509Certificate[] certs,
1608 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001609 return;
Jian Li51b844c2018-05-31 10:59:03 +09001610 }
1611 }
1612 };
1613
1614 HostnameVerifier allHostsValid = (hostname, session) -> true;
1615
1616 try {
1617 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1618 sc.init(null, trustAllCerts,
1619 new java.security.SecureRandom());
1620 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1621 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1622
1623 config.withSSLContext(sc);
1624 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001625 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001626 return null;
1627 }
1628
1629 return config;
1630 }
1631
1632 /**
1633 * Obtains the facing object with given openstack perspective.
1634 *
1635 * @param perspective keystone perspective
1636 * @return facing object
1637 */
1638 private static Facing getFacing(Perspective perspective) {
1639
1640 switch (perspective) {
1641 case PUBLIC:
1642 return Facing.PUBLIC;
1643 case ADMIN:
1644 return Facing.ADMIN;
1645 case INTERNAL:
1646 return Facing.INTERNAL;
1647 default:
1648 return null;
1649 }
1650 }
1651
1652 /**
1653 * Obtains gateway instance by giving index number.
1654 *
1655 * @param gws a collection of gateway nodes
1656 * @param index index number
1657 * @return gateway instance
1658 */
1659 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1660 Map<String, OpenstackNode> hashMap = new HashMap<>();
1661 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1662 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1663 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1664
1665 int intIndex = 0;
1666 OpenstackNode gw = null;
1667 while (iteratorKey.hasNext()) {
1668 String key = iteratorKey.next();
1669
1670 if (intIndex == index) {
1671 gw = treeMap.get(key);
1672 }
1673 intIndex++;
1674 }
1675 return gw;
1676 }
Jian Liecbf10c2019-10-02 20:36:09 +09001677
Jian Lia5c7edf2020-02-24 16:42:24 +09001678 /**
1679 * Returns the external gateway IP address with specified router information.
1680 *
1681 * @param router openstack router
1682 * @param snatOnly true for only query SNAT enabled case, false otherwise
1683 * @param osNetworkService openstack network service
1684 * @return external IP address
1685 */
1686 private static IpAddress externalGatewayIpBase(Router router, boolean snatOnly,
1687 OpenstackNetworkService osNetworkService) {
1688 if (router == null) {
1689 return null;
1690 }
1691
1692 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1693 if (externalGateway == null) {
1694 log.info("Failed to get external IP for router {} because no " +
1695 "external gateway is associated with the router",
1696 router.getId());
1697 return null;
1698 }
1699
1700 if (snatOnly) {
1701 if (!externalGateway.isEnableSnat()) {
1702 log.warn("The given router {} SNAT is configured as false", router.getId());
1703 return null;
1704 }
1705 }
1706
1707 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1708 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1709 .stream()
1710 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1711 .findAny().orElse(null);
1712
1713 if (exGatewayPort == null) {
1714 return null;
1715 }
1716
1717 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1718 .findAny().get().getIpAddress());
1719 }
1720
Jian Liecbf10c2019-10-02 20:36:09 +09001721 private static void print(String format, Object... args) {
1722 System.out.println(String.format(format, args));
1723 }
Jian Li63430202018-08-30 16:24:09 +09001724}