blob: 40d07f5a3b5b079f4f6cb30a17a0c5eaee370442 [file] [log] [blame]
Jian Li091d8d22018-02-20 10:42:06 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknetworking.util;
17
Daniel Park2ff66b42018-08-01 11:52:45 +090018import com.fasterxml.jackson.core.JsonParseException;
19import com.fasterxml.jackson.core.JsonProcessingException;
20import com.fasterxml.jackson.databind.JsonMappingException;
Jian Li091d8d22018-02-20 10:42:06 +090021import com.fasterxml.jackson.databind.JsonNode;
22import com.fasterxml.jackson.databind.ObjectMapper;
Jian Lieb9f77d2018-02-20 11:25:45 +090023import com.fasterxml.jackson.databind.node.ObjectNode;
Daniel Parka73c2362018-09-17 17:43:25 +090024import com.google.common.base.Charsets;
Jian Li24ec59f2018-05-23 19:01:25 +090025import com.google.common.base.Strings;
Daniel Parka73c2362018-09-17 17:43:25 +090026import com.google.common.collect.Lists;
Jian Li63430202018-08-30 16:24:09 +090027import org.apache.commons.codec.binary.Hex;
Jian Li51728702019-05-17 18:38:56 +090028import org.apache.commons.lang3.StringUtils;
Jian Li63430202018-08-30 16:24:09 +090029import org.apache.http.HttpException;
30import org.apache.http.HttpRequest;
31import org.apache.http.HttpResponse;
32import org.apache.http.impl.io.DefaultHttpRequestParser;
33import org.apache.http.impl.io.DefaultHttpRequestWriter;
34import org.apache.http.impl.io.DefaultHttpResponseParser;
35import org.apache.http.impl.io.DefaultHttpResponseWriter;
36import org.apache.http.impl.io.HttpTransportMetricsImpl;
37import org.apache.http.impl.io.SessionInputBufferImpl;
38import org.apache.http.impl.io.SessionOutputBufferImpl;
39import org.apache.http.io.HttpMessageWriter;
Daniel Parka73c2362018-09-17 17:43:25 +090040import org.apache.sshd.client.SshClient;
41import org.apache.sshd.client.channel.ClientChannel;
42import org.apache.sshd.client.channel.ClientChannelEvent;
43import org.apache.sshd.client.future.OpenFuture;
44import org.apache.sshd.client.session.ClientSession;
45import org.apache.sshd.common.util.io.NoCloseInputStream;
Jian Li7b8c3682019-05-12 13:57:15 +090046import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090047import org.onlab.packet.ARP;
48import org.onlab.packet.Ethernet;
49import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090050import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090051import org.onlab.packet.MacAddress;
52import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090053import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090054import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090055import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090056import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090057import org.onosproject.net.flow.DefaultTrafficTreatment;
58import org.onosproject.net.flow.TrafficTreatment;
59import org.onosproject.net.packet.DefaultOutboundPacket;
60import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090061import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090062import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090063import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li7b8c3682019-05-12 13:57:15 +090064import org.onosproject.openstacknetworking.api.OpenstackHaService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090065import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090066import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090067import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090068import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090069import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090070import org.onosproject.openstacknode.api.OpenstackAuth;
71import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090072import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090073import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51728702019-05-17 18:38:56 +090074import org.onosproject.ovsdb.controller.OvsdbClientService;
75import org.onosproject.ovsdb.controller.OvsdbController;
76import org.onosproject.ovsdb.controller.OvsdbNodeId;
Jian Li51b844c2018-05-31 10:59:03 +090077import org.openstack4j.api.OSClient;
78import org.openstack4j.api.client.IOSClientBuilder;
79import org.openstack4j.api.exceptions.AuthenticationException;
80import org.openstack4j.api.types.Facing;
81import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090082import org.openstack4j.core.transport.ObjectMapperSingleton;
83import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090084import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090085import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090086import org.openstack4j.model.network.NetFloatingIP;
87import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090088import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090089import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090090import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090091import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090092import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090093import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090094import org.slf4j.Logger;
95import org.slf4j.LoggerFactory;
96
Jian Li63430202018-08-30 16:24:09 +090097import javax.crypto.Mac;
98import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090099import javax.net.ssl.HostnameVerifier;
100import javax.net.ssl.HttpsURLConnection;
101import javax.net.ssl.SSLContext;
102import javax.net.ssl.TrustManager;
103import javax.net.ssl.X509TrustManager;
Jian Li7b8c3682019-05-12 13:57:15 +0900104import javax.ws.rs.client.Client;
105import javax.ws.rs.client.ClientBuilder;
106import javax.ws.rs.client.Entity;
107import javax.ws.rs.client.WebTarget;
108import javax.ws.rs.core.Response;
Jian Li63430202018-08-30 16:24:09 +0900109import java.io.ByteArrayInputStream;
110import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900111import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900112import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900113import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900114import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900115import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900116import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900117import java.util.HashMap;
118import java.util.Iterator;
119import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900120import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900121import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900122import java.util.Set;
123import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900124import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900125
126import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900127import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900128import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li7b8c3682019-05-12 13:57:15 +0900129import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
130import static org.apache.commons.io.IOUtils.toInputStream;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900131import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900132import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900133import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li7b8c3682019-05-12 13:57:15 +0900134import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_REST_PATH;
Daniel Parkc4d06402018-05-28 15:57:37 +0900135import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
136import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900137import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
138import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Jian Li7b8c3682019-05-12 13:57:15 +0900139import static org.onosproject.openstacknetworking.api.Constants.REST_PASSWORD;
140import static org.onosproject.openstacknetworking.api.Constants.REST_PORT;
141import static org.onosproject.openstacknetworking.api.Constants.REST_USER;
142import static org.onosproject.openstacknetworking.api.Constants.REST_UTF8;
Daniel Parkec9d1132018-08-19 11:18:03 +0900143import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700144import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900145import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900146
147/**
148 * An utility that used in openstack networking app.
149 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900150public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900151
Daniel Park95985382018-07-23 11:38:07 +0900152 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900153
Daniel Parkc4d06402018-05-28 15:57:37 +0900154 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900155 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900156 private static final String PREFIX_DEVICE_NUMBER = "s";
157 private static final String PREFIX_FUNCTION_NUMBER = "f";
158
Jian Li51b844c2018-05-31 10:59:03 +0900159 // keystone endpoint related variables
160 private static final String DOMAIN_DEFAULT = "default";
161 private static final String KEYSTONE_V2 = "v2.0";
162 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900163 private static final String SSL_TYPE = "SSL";
164
Jian Li7f024de2018-07-07 03:51:02 +0900165 private static final String PROXY_MODE = "proxy";
166 private static final String BROADCAST_MODE = "broadcast";
167
Jian Licad36c72018-09-13 17:44:54 +0900168 private static final String ENABLE = "enable";
169 private static final String DISABLE = "disable";
170
Jian Li63430202018-08-30 16:24:09 +0900171 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
172
173 private static final String HMAC_SHA256 = "HmacSHA256";
174
Jian Li24ec59f2018-05-23 19:01:25 +0900175 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
176
Daniel Parka73c2362018-09-17 17:43:25 +0900177 private static final String DL_DST = "dl_dst=";
178 private static final String NW_DST = "nw_dst=";
179 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
180 private static final String IN_PORT = "in_port=";
181 private static final String NW_SRC = "nw_src=";
182 private static final String COMMA = ",";
183 private static final String TUN_ID = "tun_id=";
184
185 private static final long TIMEOUT_MS = 5000;
186 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
187 private static final int SSH_PORT = 22;
188
Jian Li51728702019-05-17 18:38:56 +0900189 private static final int TAP_PORT_LENGTH = 11;
Jian Li62116942019-09-03 23:10:20 +0900190 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li51728702019-05-17 18:38:56 +0900191
Jian Li091d8d22018-02-20 10:42:06 +0900192 /**
193 * Prevents object instantiation from external.
194 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900195 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900196 }
197
198 /**
199 * Interprets JSON string to corresponding openstack model entity object.
200 *
Jian Li7b8c3682019-05-12 13:57:15 +0900201 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900202 * @param entityClazz openstack model entity class
203 * @return openstack model entity object
204 */
Jian Li7b8c3682019-05-12 13:57:15 +0900205 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900206 ObjectMapper mapper = new ObjectMapper();
207 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900208 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900209 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
210 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
211 return ObjectMapperSingleton.getContext(entityClazz)
212 .readerFor(entityClazz)
213 .readValue(jsonTree);
214 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900215 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900216 throw new IllegalArgumentException();
217 }
218 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900219
220 /**
221 * Converts openstack model entity object into JSON object.
222 *
223 * @param entity openstack model entity object
224 * @param entityClazz openstack model entity class
225 * @return JSON object
226 */
227 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
228 ObjectMapper mapper = new ObjectMapper();
229 try {
230 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
231 .writerFor(entityClazz)
232 .writeValueAsString(entity);
233 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900234 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900235 } catch (IOException e) {
236 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900237 throw new IllegalStateException();
238 }
239 }
Jian Li1064e4f2018-05-29 16:16:53 +0900240
241 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900242 * Obtains a floating IP associated with the given instance port.
243 *
244 * @param port instance port
245 * @param fips a collection of floating IPs
246 * @return associated floating IP
247 */
248 public static NetFloatingIP associatedFloatingIp(InstancePort port,
249 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900250 for (NetFloatingIP fip : fips) {
251 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
252 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900253 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900254 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
255 continue;
256 }
Jian Li6bc29d92018-10-02 13:55:05 +0900257 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
258 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900259 return fip;
260 }
Jian Li24ec59f2018-05-23 19:01:25 +0900261 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900262
Jian Li24ec59f2018-05-23 19:01:25 +0900263 return null;
264 }
265
266 /**
267 * Checks whether the given floating IP is associated with a VM.
268 *
269 * @param service openstack network service
270 * @param fip floating IP
271 * @return true if the given floating IP associated with a VM, false otherwise
272 */
273 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
274 NetFloatingIP fip) {
275 Port osPort = service.port(fip.getPortId());
276 if (osPort == null) {
277 return false;
278 }
279
280 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
281 Network osNet = service.network(osPort.getNetworkId());
282 if (osNet == null) {
283 final String errorFormat = ERR_FLOW + "no network(%s) exists";
284 final String error = String.format(errorFormat,
285 fip.getFloatingIpAddress(), osPort.getNetworkId());
286 throw new IllegalStateException(error);
287 }
288 return true;
289 } else {
290 return false;
291 }
292 }
293
294 /**
Jian Lia171a432018-06-11 11:52:11 +0900295 * Obtains the gateway node by instance port.
296 *
297 * @param gateways a collection of gateway nodes
298 * @param instPort instance port
299 * @return a gateway node
300 */
301 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
302 InstancePort instPort) {
303 OpenstackNode gw = null;
304 if (instPort != null && instPort.deviceId() != null) {
305 gw = getGwByComputeDevId(gateways, instPort.deviceId());
306 }
307 return gw;
308 }
309
310 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900311 * Obtains the gateway node by device in compute node. Note that the gateway
312 * node is determined by device's device identifier.
313 *
314 * @param gws a collection of gateway nodes
315 * @param deviceId device identifier
316 * @return a gateway node
317 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900318 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
319 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900320 int numOfGw = gws.size();
321
322 if (numOfGw == 0) {
323 return null;
324 }
325
326 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
327
328 return getGwByIndex(gws, gwIndex);
329 }
330
Jian Li51b844c2018-05-31 10:59:03 +0900331 /**
332 * Obtains a connected openstack client.
333 *
334 * @param osNode openstack node
335 * @return a connected openstack client
336 */
337 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900338 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900339 String endpoint = buildEndpoint(osNode);
340 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900341
Jian Li51b844c2018-05-31 10:59:03 +0900342 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900343
Jian Li51b844c2018-05-31 10:59:03 +0900344 try {
345 if (endpoint.contains(KEYSTONE_V2)) {
346 IOSClientBuilder.V2 builder = OSFactory.builderV2()
347 .endpoint(endpoint)
348 .tenantName(auth.project())
349 .credentials(auth.username(), auth.password())
350 .withConfig(config);
351
352 if (perspective != null) {
353 builder.perspective(getFacing(perspective));
354 }
355
356 return builder.authenticate();
357 } else if (endpoint.contains(KEYSTONE_V3)) {
358
359 Identifier project = Identifier.byName(auth.project());
360 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
361
362 IOSClientBuilder.V3 builder = OSFactory.builderV3()
363 .endpoint(endpoint)
364 .credentials(auth.username(), auth.password(), domain)
365 .scopeToProject(project, domain)
366 .withConfig(config);
367
368 if (perspective != null) {
369 builder.perspective(getFacing(perspective));
370 }
371
372 return builder.authenticate();
373 } else {
374 log.warn("Unrecognized keystone version type");
375 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900376 }
Jian Li51b844c2018-05-31 10:59:03 +0900377 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900378 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900379 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900380 }
Jian Li1064e4f2018-05-29 16:16:53 +0900381 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900382
383 /**
384 * Extract the interface name with the supplied port.
385 *
386 * @param port port
387 * @return interface name
388 */
389 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900390 String intfName;
391
Daniel Park95985382018-07-23 11:38:07 +0900392 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900393 log.error("Port profile is not found");
394 return null;
395 }
396
Daniel Park95985382018-07-23 11:38:07 +0900397 if (!port.getProfile().containsKey(PCISLOT) ||
398 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900399 log.error("Failed to retrieve the interface name because of no " +
400 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900401 return null;
402 }
Jian Li51b844c2018-05-31 10:59:03 +0900403
Daniel Parkff178ba2018-11-23 15:57:24 +0900404 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
405
406 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
407 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
408 return UNSUPPORTED_VENDOR;
409 }
410
411 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
412
Daniel Parkc4d06402018-05-28 15:57:37 +0900413 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
414 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
415
416 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
417 .split(":")[2]
418 .split("\\.")[0];
419 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
420
421 String functionNumHex = port.getProfile().get(PCISLOT).toString()
422 .split(":")[2]
423 .split("\\.")[1];
424 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
425
Daniel Parkc4d06402018-05-28 15:57:37 +0900426 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
427 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
428 } else {
429 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
430 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
431 }
432
433 return intfName;
434 }
Jian Li51b844c2018-05-31 10:59:03 +0900435
436 /**
Daniel Park95f73312018-07-31 15:48:34 +0900437 * Check if the given interface is added to the given device or not.
438 *
439 * @param deviceId device ID
440 * @param intfName interface name
441 * @param deviceService device service
442 * @return true if the given interface is added to the given device or false otherwise
443 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900444 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
445 String intfName,
446 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900447 checkNotNull(deviceId);
448 checkNotNull(intfName);
449
Jian Li5ecfd1a2018-12-10 11:41:03 +0900450 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
451 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900452 }
453
454 /**
Jian Li0b564282018-06-20 00:50:53 +0900455 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900456 *
457 * @param osPort port
458 * @param adminService openstack admin service
459 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900460 public static void addRouterIface(Port osPort,
461 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900462 osPort.getFixedIps().forEach(p -> {
463 JsonNode jsonTree = new ObjectMapper().createObjectNode()
464 .put("id", osPort.getDeviceId())
465 .put("tenant_id", osPort.getTenantId())
466 .put("subnet_id", p.getSubnetId())
467 .put("port_id", osPort.getId());
468 try {
469 RouterInterface rIface = getContext(NeutronRouterInterface.class)
470 .readerFor(NeutronRouterInterface.class)
471 .readValue(jsonTree);
472 if (adminService.routerInterface(rIface.getPortId()) != null) {
473 adminService.updateRouterInterface(rIface);
474 } else {
475 adminService.addRouterInterface(rIface);
476 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900477 } catch (IOException e) {
478 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900479 }
480 });
481 }
482
483 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900484 * Obtains the property value with specified property key name.
485 *
486 * @param properties a collection of properties
487 * @param name key name
488 * @return mapping value
489 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900490 public static String getPropertyValue(Set<ConfigProperty> properties,
491 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900492 Optional<ConfigProperty> property =
493 properties.stream().filter(p -> p.name().equals(name)).findFirst();
494 return property.map(ConfigProperty::value).orElse(null);
495 }
496
497 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900498 * Obtains the boolean property value with specified property key name.
499 *
500 * @param properties a collection of properties
501 * @param name key name
502 * @return mapping value
503 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900504 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
505 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900506 Optional<ConfigProperty> property =
507 properties.stream().filter(p -> p.name().equals(name)).findFirst();
508
509 return property.map(ConfigProperty::asBoolean).orElse(false);
510 }
511
512 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900513 * Prints out the JSON string in pretty format.
514 *
515 * @param mapper Object mapper
516 * @param jsonString JSON string
517 * @return pretty formatted JSON string
518 */
519 public static String prettyJson(ObjectMapper mapper, String jsonString) {
520 try {
521 Object jsonObject = mapper.readValue(jsonString, Object.class);
522 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900523 } catch (JsonParseException e) {
524 log.debug("JsonParseException caused by {}", e);
525 } catch (JsonMappingException e) {
526 log.debug("JsonMappingException caused by {}", e);
527 } catch (JsonProcessingException e) {
528 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900529 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900530 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900531 }
532 return null;
533 }
534
535 /**
Jian Li7f024de2018-07-07 03:51:02 +0900536 * Checks the validity of ARP mode.
537 *
538 * @param arpMode ARP mode
539 * @return returns true if the ARP mode is valid, false otherwise
540 */
541 public static boolean checkArpMode(String arpMode) {
542
543 if (isNullOrEmpty(arpMode)) {
544 return false;
545 } else {
546 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
547 }
548 }
549
550 /**
Jian Licad36c72018-09-13 17:44:54 +0900551 * Checks the validity of activation flag.
552 *
553 * @param activationFlag activation flag
554 * @return returns true if the activation flag is valid, false otherwise
555 */
556 public static boolean checkActivationFlag(String activationFlag) {
557
558 switch (activationFlag) {
559 case ENABLE:
560 return true;
561 case DISABLE:
562 return false;
563 default:
564 throw new IllegalArgumentException("The given activation flag is not valid!");
565 }
566 }
567
568 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900569 * Swaps current location with old location info.
570 * The revised instance port will be used to mod the flow rules after migration.
571 *
572 * @param instPort instance port
573 * @return location swapped instance port
574 */
575 public static InstancePort swapStaleLocation(InstancePort instPort) {
576 return DefaultInstancePort.builder()
577 .deviceId(instPort.oldDeviceId())
578 .portNumber(instPort.oldPortNumber())
579 .state(instPort.state())
580 .ipAddress(instPort.ipAddress())
581 .macAddress(instPort.macAddress())
582 .networkId(instPort.networkId())
583 .portId(instPort.portId())
584 .build();
585 }
586
587 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900588 * Compares two router interfaces are equal.
589 * Will be remove this after Openstack4j implements equals.
590 *
591 * @param routerInterface1 router interface
592 * @param routerInterface2 router interface
593 * @return returns true if two router interfaces are equal, false otherwise
594 */
Jian Li63430202018-08-30 16:24:09 +0900595 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
596 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900597 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
598 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
599 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
600 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
601 }
602
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900603 /**
604 * Returns the vnic type of given port.
605 *
606 * @param portName port name
607 * @return vnit type
608 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900609 public static VnicType vnicType(String portName) {
610 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
611 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
612 return VnicType.NORMAL;
613 } else if (isDirectPort(portName)) {
614 return VnicType.DIRECT;
615 } else {
616 return VnicType.UNSUPPORTED;
617 }
618 }
619
Jian Li63430202018-08-30 16:24:09 +0900620 /**
621 * Deserializes raw payload into HttpRequest object.
622 *
623 * @param rawData raw http payload
624 * @return HttpRequest object
625 */
626 public static HttpRequest parseHttpRequest(byte[] rawData) {
627 SessionInputBufferImpl sessionInputBuffer =
628 new SessionInputBufferImpl(
629 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
630 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900631 DefaultHttpRequestParser requestParser =
632 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900633 try {
634 return requestParser.parse();
635 } catch (IOException | HttpException e) {
636 log.warn("Failed to parse HttpRequest, due to {}", e);
637 }
638
639 return null;
640 }
641
642 /**
643 * Serializes HttpRequest object to byte array.
644 *
645 * @param request http request object
646 * @return byte array
647 */
648 public static byte[] unparseHttpRequest(HttpRequest request) {
649 try {
650 SessionOutputBufferImpl sessionOutputBuffer =
651 new SessionOutputBufferImpl(
652 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
653
654 ByteArrayOutputStream baos = new ByteArrayOutputStream();
655 sessionOutputBuffer.bind(baos);
656
Jian Li5ecfd1a2018-12-10 11:41:03 +0900657 HttpMessageWriter<HttpRequest> requestWriter =
658 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900659 requestWriter.write(request);
660 sessionOutputBuffer.flush();
661
662 return baos.toByteArray();
663 } catch (HttpException | IOException e) {
664 log.warn("Failed to unparse HttpRequest, due to {}", e);
665 }
666
667 return null;
668 }
669
670 /**
671 * Deserializes raw payload into HttpResponse object.
672 *
673 * @param rawData raw http payload
674 * @return HttpResponse object
675 */
676 public static HttpResponse parseHttpResponse(byte[] rawData) {
677 SessionInputBufferImpl sessionInputBuffer =
678 new SessionInputBufferImpl(
679 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
680 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900681 DefaultHttpResponseParser responseParser =
682 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900683 try {
684 return responseParser.parse();
685 } catch (IOException | HttpException e) {
686 log.warn("Failed to parse HttpResponse, due to {}", e);
687 }
688
689 return null;
690 }
691
692 /**
693 * Serializes HttpResponse header to byte array.
694 *
695 * @param response http response object
696 * @return byte array
697 */
698 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
699 try {
700 SessionOutputBufferImpl sessionOutputBuffer =
701 new SessionOutputBufferImpl(
702 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
703
704 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
705 sessionOutputBuffer.bind(headerBaos);
706
707 HttpMessageWriter<HttpResponse> responseWriter =
708 new DefaultHttpResponseWriter(sessionOutputBuffer);
709 responseWriter.write(response);
710 sessionOutputBuffer.flush();
711
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900712 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900713
714 return headerBaos.toByteArray();
715 } catch (IOException | HttpException e) {
716 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
717 }
718
719 return null;
720 }
721
722 /**
723 * Serializes HttpResponse object to byte array.
724 *
725 * @param response http response object
726 * @return byte array
727 */
728 public static byte[] unparseHttpResponseBody(HttpResponse response) {
729 try {
730 ByteArrayOutputStream baos = new ByteArrayOutputStream();
731 response.getEntity().writeTo(baos);
732
733 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900734 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900735
736 return baos.toByteArray();
737 } catch (IOException e) {
738 log.warn("Failed to unparse HttpResponse, due to {}", e);
739 }
740
741 return null;
742 }
743
744 /**
745 * Encodes the given data using HmacSHA256 encryption method with given secret key.
746 *
747 * @param key secret key
748 * @param data data to be encrypted
749 * @return Hmac256 encrypted data
750 */
751 public static String hmacEncrypt(String key, String data) {
752 try {
753 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
754 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
755 sha256Hmac.init(secretKey);
756 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
757 } catch (Exception e) {
758 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
759 }
760 return null;
761 }
762
Daniel Parka73c2362018-09-17 17:43:25 +0900763 /**
764 * Creates flow trace request string.
765 *
766 * @param srcIp src ip address
767 * @param dstIp dst ip address
768 * @param srcInstancePort src instance port
769 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900770 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900771 * @return flow trace request string
772 */
773 public static String traceRequestString(String srcIp,
774 String dstIp,
775 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900776 OpenstackNetworkService osNetService,
777 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900778
779 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
780
781 if (uplink) {
782
783 requestStringBuilder.append(COMMA)
784 .append(IN_PORT)
785 .append(srcInstancePort.portNumber().toString())
786 .append(COMMA)
787 .append(NW_SRC)
788 .append(srcIp)
789 .append(COMMA);
790
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900791 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900792 Type netType = osNetService.networkType(srcInstancePort.networkId());
793 if (netType == Type.VXLAN || netType == Type.GRE ||
794 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900795 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900796 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900797 requestStringBuilder.append(DL_DST)
798 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900799 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
800 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900801 requestStringBuilder.append(DL_DST)
802 .append(DEFAULT_GATEWAY_MAC_STR)
803 .append(COMMA);
804 }
805 } else {
806 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900807 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900808 }
809 }
810
811 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900812 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900813 .append("\n");
814 } else {
815 requestStringBuilder.append(COMMA)
816 .append(NW_SRC)
817 .append(dstIp)
818 .append(COMMA);
819
Jian Li621f73c2018-12-15 01:49:22 +0900820 Type netType = osNetService.networkType(srcInstancePort.networkId());
821
822 if (netType == Type.VXLAN || netType == Type.GRE ||
823 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900824 requestStringBuilder.append(TUN_ID)
825 .append(osNetService.segmentId(srcInstancePort.networkId()))
826 .append(COMMA);
827 }
828 requestStringBuilder.append(NW_DST)
829 .append(srcIp)
830 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900831 }
832
833 return requestStringBuilder.toString();
834 }
835
836 /**
837 * Sends flow trace string to node.
838 *
839 * @param requestString reqeust string
840 * @param node src node
841 * @return flow trace result in string format
842 */
843 public static String sendTraceRequestToNode(String requestString,
844 OpenstackNode node) {
845 String traceResult = null;
846 OpenstackSshAuth sshAuth = node.sshAuthInfo();
847
848 try (SshClient client = SshClient.setUpDefaultClient()) {
849 client.start();
850
851 try (ClientSession session = client
852 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
853 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
854 session.addPasswordIdentity(sshAuth.password());
855 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
856
857
858 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
859
860 log.debug("requestString: {}", requestString);
861 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900862 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900863
Jian Li5ecfd1a2018-12-10 11:41:03 +0900864 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900865 OutputStream errStream = new ByteArrayOutputStream();
866
867 channel.setIn(new NoCloseInputStream(inputStream));
868 channel.setErr(errStream);
869 channel.setOut(outputStream);
870
871 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
872 eventList.add(ClientChannelEvent.OPENED);
873
874 OpenFuture channelFuture = channel.open();
875
876 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
877
878 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
879
880 while (!channelFuture.isOpened()) {
881 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
882 log.error("Failed to open channel");
883 return null;
884 }
885 }
886 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
887
Jian Li5ecfd1a2018-12-10 11:41:03 +0900888 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900889
890 channel.close();
891 }
892 } finally {
893 session.close();
894 }
895 } finally {
896 client.stop();
897 }
898
899 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900900 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900901 }
902
903 return traceResult;
904 }
905
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900906 /**
907 * Returns the floating ip with supplied instance port.
908 *
909 * @param instancePort instance port
910 * @param osRouterAdminService openstack router admin service
911 * @return floating ip
912 */
913 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900914 OpenstackRouterAdminService
915 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900916 return osRouterAdminService.floatingIps().stream()
917 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
918 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
919 .findAny().orElse(null);
920 }
921
922 /**
923 * Sends GARP packet with supplied floating ip information.
924 *
925 * @param floatingIP floating ip
926 * @param instancePort instance port
927 * @param vlanId vlain id
928 * @param gatewayNode gateway node
929 * @param packetService packet service
930 */
Jian Li32b03622018-11-06 17:54:24 +0900931 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
932 InstancePort instancePort,
933 VlanId vlanId,
934 OpenstackNode gatewayNode,
935 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900936 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
937
938 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
939 .setOutput(gatewayNode.uplinkPortNum()).build();
940
941 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
942 ByteBuffer.wrap(ethernet.serialize())));
943 }
944
945 /**
946 * Returns the external peer router with supplied network information.
947 *
948 * @param network network
949 * @param osNetworkService openstack network service
950 * @param osRouterAdminService openstack router admin service
951 * @return external peer router
952 */
953 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900954 OpenstackNetworkService
955 osNetworkService,
956 OpenstackRouterAdminService
957 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900958 if (network == null) {
959 return null;
960 }
961
Jian Lie6e609f2019-05-14 17:45:54 +0900962 Subnet subnet = osNetworkService.subnets(network.getId())
963 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900964
965 if (subnet == null) {
966 return null;
967 }
968
969 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
970 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
971 .findAny().orElse(null);
972 if (osRouterIface == null) {
973 return null;
974 }
975
976 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900977 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900978 return null;
979 }
980
981 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
982 return osNetworkService.externalPeerRouter(exGatewayInfo);
983
984 }
985
Jian Liebde74d2018-11-14 00:18:57 +0900986 /**
987 * Returns the external peer router with specified subnet information.
988 *
989 * @param subnet openstack subnet
990 * @param osRouterService openstack router service
991 * @param osNetworkService openstack network service
992 * @return external peer router
993 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900994 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
995 OpenstackRouterService
996 osRouterService,
997 OpenstackNetworkService
998 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900999 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
1000 if (osRouter == null) {
1001 return null;
1002 }
1003 if (osRouter.getExternalGatewayInfo() == null) {
1004 // this router does not have external connectivity
1005 log.trace("router({}) has no external gateway",
1006 osRouter.getName());
1007 return null;
1008 }
1009
1010 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1011 }
1012
1013 /**
1014 * Returns the external ip address with specified router information.
1015 *
1016 * @param srcSubnet source subnet
1017 * @param osRouterService openstack router service
1018 * @param osNetworkService openstack network service
1019 * @return external ip address
1020 */
1021 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001022 OpenstackRouterService
1023 osRouterService,
1024 OpenstackNetworkService
1025 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001026
1027 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1028
1029 if (osRouter.getExternalGatewayInfo() == null) {
1030 // this router does not have external connectivity
1031 log.trace("router({}) has no external gateway",
1032 osRouter.getName());
1033 return null;
1034 }
1035
1036 return getExternalIp(osRouter, osNetworkService);
1037 }
1038
1039 /**
1040 * Returns the external ip address with specified router information.
1041 *
1042 * @param router openstack router
1043 * @param osNetworkService openstack network service
1044 * @return external ip address
1045 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001046 public static IpAddress getExternalIp(Router router,
1047 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001048 if (router == null) {
1049 return null;
1050 }
1051
1052 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1053 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001054 log.trace("Failed to get externalIp for router {} because " +
1055 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001056 router.getId());
1057 return null;
1058 }
1059
1060 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1061 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1062 .stream()
1063 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1064 .findAny().orElse(null);
1065
1066 if (exGatewayPort == null) {
1067 return null;
1068 }
1069
1070 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1071 .findAny().get().getIpAddress());
1072 }
1073
Jian Li2d68c192018-12-13 15:52:59 +09001074 /**
1075 * Returns the tunnel port number with specified net ID and openstack node.
1076 *
1077 * @param netId network ID
1078 * @param netService network service
1079 * @param osNode openstack node
1080 * @return tunnel port number
1081 */
1082 public static PortNumber tunnelPortNumByNetId(String netId,
1083 OpenstackNetworkService netService,
1084 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001085 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001086
1087 if (netType == null) {
1088 return null;
1089 }
1090
1091 return tunnelPortNumByNetType(netType, osNode);
1092 }
1093
1094 /**
1095 * Returns the tunnel port number with specified net type and openstack node.
1096 *
1097 * @param netType network type
1098 * @param osNode openstack node
1099 * @return tunnel port number
1100 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001101 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001102 switch (netType) {
1103 case VXLAN:
1104 return osNode.vxlanTunnelPortNum();
1105 case GRE:
1106 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001107 case GENEVE:
1108 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001109 default:
1110 return null;
1111 }
1112 }
1113
Jian Li7b8c3682019-05-12 13:57:15 +09001114 /**
1115 * Returns the REST URL of active node.
1116 *
1117 * @param haService openstack HA service
1118 * @return REST URL of active node
1119 */
1120 public static String getActiveUrl(OpenstackHaService haService) {
1121 return "http://" + haService.getActiveIp().toString() + ":" +
1122 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1123 }
1124
1125 /**
1126 * Returns the REST client instance with given resource path.
1127 *
1128 * @param haService openstack HA service
1129 * @param resourcePath resource path
1130 * @return REST client instance
1131 */
1132 public static WebTarget getActiveClient(OpenstackHaService haService,
1133 String resourcePath) {
1134 HttpAuthenticationFeature feature =
1135 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1136 Client client = ClientBuilder.newClient().register(feature);
1137 return client.target(getActiveUrl(haService)).path(resourcePath);
1138 }
1139
1140 /**
1141 * Returns the post response from the active node.
1142 *
1143 * @param haService openstack HA service
1144 * @param resourcePath resource path
1145 * @param input input
1146 * @return post response
1147 */
1148 public static Response syncPost(OpenstackHaService haService,
1149 String resourcePath,
1150 String input) {
1151
1152 log.debug("Sync POST request with {} on {}",
1153 haService.getActiveIp().toString(), resourcePath);
1154
1155 return getActiveClient(haService, resourcePath)
1156 .request(APPLICATION_JSON_TYPE)
1157 .post(Entity.json(input));
1158 }
1159
1160 /**
1161 * Returns the put response from the active node.
1162 *
1163 * @param haService openstack HA service
1164 * @param resourcePath resource path
1165 * @param id resource identifier
1166 * @param input input
1167 * @return put response
1168 */
1169 public static Response syncPut(OpenstackHaService haService,
1170 String resourcePath,
1171 String id, String input) {
1172 return syncPut(haService, resourcePath, null, id, input);
1173 }
1174
1175 /**
1176 * Returns the put response from the active node.
1177 *
1178 * @param haService openstack HA service
1179 * @param resourcePath resource path
1180 * @param id resource identifier
1181 * @param suffix resource suffix
1182 * @param input input
1183 * @return put response
1184 */
1185 public static Response syncPut(OpenstackHaService haService,
1186 String resourcePath,
1187 String suffix,
1188 String id, String input) {
1189
1190 log.debug("Sync PUT request with {} on {}",
1191 haService.getActiveIp().toString(), resourcePath);
1192
1193 String pathStr = "/" + id;
1194
1195 if (suffix != null) {
1196 pathStr += "/" + suffix;
1197 }
1198
1199 return getActiveClient(haService, resourcePath)
1200 .path(pathStr)
1201 .request(APPLICATION_JSON_TYPE)
1202 .put(Entity.json(input));
1203 }
1204
1205 /**
1206 * Returns the delete response from the active node.
1207 *
1208 * @param haService openstack HA service
1209 * @param resourcePath resource path
1210 * @param id resource identifier
1211 * @return delete response
1212 */
1213 public static Response syncDelete(OpenstackHaService haService,
1214 String resourcePath,
1215 String id) {
1216
1217 log.debug("Sync DELETE request with {} on {}",
1218 haService.getActiveIp().toString(), resourcePath);
1219
1220 return getActiveClient(haService, resourcePath)
1221 .path("/" + id)
1222 .request(APPLICATION_JSON_TYPE)
1223 .delete();
1224 }
1225
Jian Li51728702019-05-17 18:38:56 +09001226 /**
1227 * Gets the ovsdb client with supplied openstack node.
1228 *
1229 * @param node openstack node
1230 * @param ovsdbPort openvswitch DB port number
1231 * @param controller openvswitch DB controller instance
1232 * @return ovsdb client instance
1233 */
1234 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1235 OvsdbController controller) {
1236 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1237 return controller.getOvsdbClient(ovsdb);
1238 }
1239
1240 /**
1241 * Obtains the name of interface attached to the openstack VM.
1242 *
1243 * @param portId openstack port identifier
1244 * @return name of interface
1245 */
1246 public static String ifaceNameFromOsPortId(String portId) {
1247 if (portId != null) {
1248 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1249 }
1250
1251 return null;
1252 }
1253
Jian Li5ecfd1a2018-12-10 11:41:03 +09001254 private static Router getRouterFromSubnet(Subnet subnet,
1255 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001256 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1257 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1258 .findAny().orElse(null);
1259 if (osRouterIface == null) {
1260 return null;
1261 }
1262
1263 return osRouterService.router(osRouterIface.getId());
1264 }
1265
Daniel Park7e8c4d82018-08-13 23:47:49 +09001266 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001267 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001268 }
1269
Daniel Park2ff66b42018-08-01 11:52:45 +09001270 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001271 * Returns GARP packet with supplied floating ip and instance port information.
1272 *
1273 * @param floatingIP floating ip
1274 * @param instancePort instance port
1275 * @param vlanId vlan id
1276 * @return GARP packet
1277 */
1278 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1279 InstancePort instancePort,
1280 VlanId vlanId) {
1281 Ethernet ethernet = new Ethernet();
1282 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1283 ethernet.setSourceMACAddress(instancePort.macAddress());
1284 ethernet.setEtherType(Ethernet.TYPE_ARP);
1285 ethernet.setVlanID(vlanId.id());
1286
1287 ARP arp = new ARP();
1288 arp.setOpCode(ARP.OP_REPLY);
1289 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1290 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1291
1292 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1293 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1294
1295 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1296 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1297
Jian Li5ecfd1a2018-12-10 11:41:03 +09001298 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1299 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001300
1301 ethernet.setPayload(arp);
1302
1303 return ethernet;
1304 }
1305
1306 /**
Jian Li62116942019-09-03 23:10:20 +09001307 * Re-structures the OVS port name.
1308 * The length of OVS port name should be not large than 15.
1309 *
1310 * @param portName original port name
1311 * @return re-structured OVS port name
1312 */
1313 public static String structurePortName(String portName) {
1314
1315 // The size of OVS port name should not be larger than 15
1316 if (portName.length() > PORT_NAME_MAX_LENGTH) {
1317 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
1318 }
1319
1320 return portName;
1321 }
1322
1323 /**
Jian Li51b844c2018-05-31 10:59:03 +09001324 * Builds up and a complete endpoint URL from gateway node.
1325 *
1326 * @param node gateway node
1327 * @return a complete endpoint URL
1328 */
1329 private static String buildEndpoint(OpenstackNode node) {
1330
Jian Lic704b672018-09-04 18:52:53 +09001331 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001332
1333 StringBuilder endpointSb = new StringBuilder();
1334 endpointSb.append(auth.protocol().name().toLowerCase());
1335 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001336 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001337 return endpointSb.toString();
1338 }
1339
1340 /**
1341 * Obtains the SSL config without verifying the certification.
1342 *
1343 * @return SSL config
1344 */
1345 private static Config getSslConfig() {
1346 // we bypass the SSL certification verification for now
1347 // TODO: verify server side SSL using a given certification
1348 Config config = Config.newConfig().withSSLVerificationDisabled();
1349
1350 TrustManager[] trustAllCerts = new TrustManager[]{
1351 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001352 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001353 public X509Certificate[] getAcceptedIssuers() {
1354 return null;
1355 }
1356
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001357 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001358 public void checkClientTrusted(X509Certificate[] certs,
1359 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001360 return;
Jian Li51b844c2018-05-31 10:59:03 +09001361 }
1362
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001363 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001364 public void checkServerTrusted(X509Certificate[] certs,
1365 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001366 return;
Jian Li51b844c2018-05-31 10:59:03 +09001367 }
1368 }
1369 };
1370
1371 HostnameVerifier allHostsValid = (hostname, session) -> true;
1372
1373 try {
1374 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1375 sc.init(null, trustAllCerts,
1376 new java.security.SecureRandom());
1377 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1378 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1379
1380 config.withSSLContext(sc);
1381 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001382 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001383 return null;
1384 }
1385
1386 return config;
1387 }
1388
1389 /**
1390 * Obtains the facing object with given openstack perspective.
1391 *
1392 * @param perspective keystone perspective
1393 * @return facing object
1394 */
1395 private static Facing getFacing(Perspective perspective) {
1396
1397 switch (perspective) {
1398 case PUBLIC:
1399 return Facing.PUBLIC;
1400 case ADMIN:
1401 return Facing.ADMIN;
1402 case INTERNAL:
1403 return Facing.INTERNAL;
1404 default:
1405 return null;
1406 }
1407 }
1408
1409 /**
1410 * Obtains gateway instance by giving index number.
1411 *
1412 * @param gws a collection of gateway nodes
1413 * @param index index number
1414 * @return gateway instance
1415 */
1416 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1417 Map<String, OpenstackNode> hashMap = new HashMap<>();
1418 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1419 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1420 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1421
1422 int intIndex = 0;
1423 OpenstackNode gw = null;
1424 while (iteratorKey.hasNext()) {
1425 String key = iteratorKey.next();
1426
1427 if (intIndex == index) {
1428 gw = treeMap.get(key);
1429 }
1430 intIndex++;
1431 }
1432 return gw;
1433 }
Jian Li63430202018-08-30 16:24:09 +09001434}