blob: 5402ca57512006690dd319094f45f8638a5e8f16 [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;
190
Jian Li091d8d22018-02-20 10:42:06 +0900191 /**
192 * Prevents object instantiation from external.
193 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900194 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900195 }
196
197 /**
198 * Interprets JSON string to corresponding openstack model entity object.
199 *
Jian Li7b8c3682019-05-12 13:57:15 +0900200 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900201 * @param entityClazz openstack model entity class
202 * @return openstack model entity object
203 */
Jian Li7b8c3682019-05-12 13:57:15 +0900204 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900205 ObjectMapper mapper = new ObjectMapper();
206 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900207 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900208 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
209 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
210 return ObjectMapperSingleton.getContext(entityClazz)
211 .readerFor(entityClazz)
212 .readValue(jsonTree);
213 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900214 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900215 throw new IllegalArgumentException();
216 }
217 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900218
219 /**
220 * Converts openstack model entity object into JSON object.
221 *
222 * @param entity openstack model entity object
223 * @param entityClazz openstack model entity class
224 * @return JSON object
225 */
226 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
227 ObjectMapper mapper = new ObjectMapper();
228 try {
229 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
230 .writerFor(entityClazz)
231 .writeValueAsString(entity);
232 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900233 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900234 } catch (IOException e) {
235 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900236 throw new IllegalStateException();
237 }
238 }
Jian Li1064e4f2018-05-29 16:16:53 +0900239
240 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900241 * Obtains a floating IP associated with the given instance port.
242 *
243 * @param port instance port
244 * @param fips a collection of floating IPs
245 * @return associated floating IP
246 */
247 public static NetFloatingIP associatedFloatingIp(InstancePort port,
248 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900249 for (NetFloatingIP fip : fips) {
250 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
251 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900252 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900253 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
254 continue;
255 }
Jian Li6bc29d92018-10-02 13:55:05 +0900256 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
257 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900258 return fip;
259 }
Jian Li24ec59f2018-05-23 19:01:25 +0900260 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900261
Jian Li24ec59f2018-05-23 19:01:25 +0900262 return null;
263 }
264
265 /**
266 * Checks whether the given floating IP is associated with a VM.
267 *
268 * @param service openstack network service
269 * @param fip floating IP
270 * @return true if the given floating IP associated with a VM, false otherwise
271 */
272 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
273 NetFloatingIP fip) {
274 Port osPort = service.port(fip.getPortId());
275 if (osPort == null) {
276 return false;
277 }
278
279 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
280 Network osNet = service.network(osPort.getNetworkId());
281 if (osNet == null) {
282 final String errorFormat = ERR_FLOW + "no network(%s) exists";
283 final String error = String.format(errorFormat,
284 fip.getFloatingIpAddress(), osPort.getNetworkId());
285 throw new IllegalStateException(error);
286 }
287 return true;
288 } else {
289 return false;
290 }
291 }
292
293 /**
Jian Lia171a432018-06-11 11:52:11 +0900294 * Obtains the gateway node by instance port.
295 *
296 * @param gateways a collection of gateway nodes
297 * @param instPort instance port
298 * @return a gateway node
299 */
300 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
301 InstancePort instPort) {
302 OpenstackNode gw = null;
303 if (instPort != null && instPort.deviceId() != null) {
304 gw = getGwByComputeDevId(gateways, instPort.deviceId());
305 }
306 return gw;
307 }
308
309 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900310 * Obtains the gateway node by device in compute node. Note that the gateway
311 * node is determined by device's device identifier.
312 *
313 * @param gws a collection of gateway nodes
314 * @param deviceId device identifier
315 * @return a gateway node
316 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900317 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
318 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900319 int numOfGw = gws.size();
320
321 if (numOfGw == 0) {
322 return null;
323 }
324
325 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
326
327 return getGwByIndex(gws, gwIndex);
328 }
329
Jian Li51b844c2018-05-31 10:59:03 +0900330 /**
331 * Obtains a connected openstack client.
332 *
333 * @param osNode openstack node
334 * @return a connected openstack client
335 */
336 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900337 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900338 String endpoint = buildEndpoint(osNode);
339 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900340
Jian Li51b844c2018-05-31 10:59:03 +0900341 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900342
Jian Li51b844c2018-05-31 10:59:03 +0900343 try {
344 if (endpoint.contains(KEYSTONE_V2)) {
345 IOSClientBuilder.V2 builder = OSFactory.builderV2()
346 .endpoint(endpoint)
347 .tenantName(auth.project())
348 .credentials(auth.username(), auth.password())
349 .withConfig(config);
350
351 if (perspective != null) {
352 builder.perspective(getFacing(perspective));
353 }
354
355 return builder.authenticate();
356 } else if (endpoint.contains(KEYSTONE_V3)) {
357
358 Identifier project = Identifier.byName(auth.project());
359 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
360
361 IOSClientBuilder.V3 builder = OSFactory.builderV3()
362 .endpoint(endpoint)
363 .credentials(auth.username(), auth.password(), domain)
364 .scopeToProject(project, domain)
365 .withConfig(config);
366
367 if (perspective != null) {
368 builder.perspective(getFacing(perspective));
369 }
370
371 return builder.authenticate();
372 } else {
373 log.warn("Unrecognized keystone version type");
374 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900375 }
Jian Li51b844c2018-05-31 10:59:03 +0900376 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900377 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900378 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900379 }
Jian Li1064e4f2018-05-29 16:16:53 +0900380 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900381
382 /**
383 * Extract the interface name with the supplied port.
384 *
385 * @param port port
386 * @return interface name
387 */
388 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900389 String intfName;
390
Daniel Park95985382018-07-23 11:38:07 +0900391 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900392 log.error("Port profile is not found");
393 return null;
394 }
395
Daniel Park95985382018-07-23 11:38:07 +0900396 if (!port.getProfile().containsKey(PCISLOT) ||
397 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900398 log.error("Failed to retrieve the interface name because of no " +
399 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900400 return null;
401 }
Jian Li51b844c2018-05-31 10:59:03 +0900402
Daniel Parkff178ba2018-11-23 15:57:24 +0900403 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
404
405 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
406 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
407 return UNSUPPORTED_VENDOR;
408 }
409
410 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
411
Daniel Parkc4d06402018-05-28 15:57:37 +0900412 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
413 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
414
415 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
416 .split(":")[2]
417 .split("\\.")[0];
418 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
419
420 String functionNumHex = port.getProfile().get(PCISLOT).toString()
421 .split(":")[2]
422 .split("\\.")[1];
423 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
424
Daniel Parkc4d06402018-05-28 15:57:37 +0900425 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
426 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
427 } else {
428 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
429 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
430 }
431
432 return intfName;
433 }
Jian Li51b844c2018-05-31 10:59:03 +0900434
435 /**
Daniel Park95f73312018-07-31 15:48:34 +0900436 * Check if the given interface is added to the given device or not.
437 *
438 * @param deviceId device ID
439 * @param intfName interface name
440 * @param deviceService device service
441 * @return true if the given interface is added to the given device or false otherwise
442 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900443 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
444 String intfName,
445 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900446 checkNotNull(deviceId);
447 checkNotNull(intfName);
448
Jian Li5ecfd1a2018-12-10 11:41:03 +0900449 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
450 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900451 }
452
453 /**
Jian Li0b564282018-06-20 00:50:53 +0900454 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900455 *
456 * @param osPort port
457 * @param adminService openstack admin service
458 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900459 public static void addRouterIface(Port osPort,
460 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900461 osPort.getFixedIps().forEach(p -> {
462 JsonNode jsonTree = new ObjectMapper().createObjectNode()
463 .put("id", osPort.getDeviceId())
464 .put("tenant_id", osPort.getTenantId())
465 .put("subnet_id", p.getSubnetId())
466 .put("port_id", osPort.getId());
467 try {
468 RouterInterface rIface = getContext(NeutronRouterInterface.class)
469 .readerFor(NeutronRouterInterface.class)
470 .readValue(jsonTree);
471 if (adminService.routerInterface(rIface.getPortId()) != null) {
472 adminService.updateRouterInterface(rIface);
473 } else {
474 adminService.addRouterInterface(rIface);
475 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900476 } catch (IOException e) {
477 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900478 }
479 });
480 }
481
482 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900483 * Obtains the property value with specified property key name.
484 *
485 * @param properties a collection of properties
486 * @param name key name
487 * @return mapping value
488 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900489 public static String getPropertyValue(Set<ConfigProperty> properties,
490 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900491 Optional<ConfigProperty> property =
492 properties.stream().filter(p -> p.name().equals(name)).findFirst();
493 return property.map(ConfigProperty::value).orElse(null);
494 }
495
496 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900497 * Obtains the boolean property value with specified property key name.
498 *
499 * @param properties a collection of properties
500 * @param name key name
501 * @return mapping value
502 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900503 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
504 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900505 Optional<ConfigProperty> property =
506 properties.stream().filter(p -> p.name().equals(name)).findFirst();
507
508 return property.map(ConfigProperty::asBoolean).orElse(false);
509 }
510
511 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900512 * Prints out the JSON string in pretty format.
513 *
514 * @param mapper Object mapper
515 * @param jsonString JSON string
516 * @return pretty formatted JSON string
517 */
518 public static String prettyJson(ObjectMapper mapper, String jsonString) {
519 try {
520 Object jsonObject = mapper.readValue(jsonString, Object.class);
521 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900522 } catch (JsonParseException e) {
523 log.debug("JsonParseException caused by {}", e);
524 } catch (JsonMappingException e) {
525 log.debug("JsonMappingException caused by {}", e);
526 } catch (JsonProcessingException e) {
527 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900528 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900529 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900530 }
531 return null;
532 }
533
534 /**
Jian Li7f024de2018-07-07 03:51:02 +0900535 * Checks the validity of ARP mode.
536 *
537 * @param arpMode ARP mode
538 * @return returns true if the ARP mode is valid, false otherwise
539 */
540 public static boolean checkArpMode(String arpMode) {
541
542 if (isNullOrEmpty(arpMode)) {
543 return false;
544 } else {
545 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
546 }
547 }
548
549 /**
Jian Licad36c72018-09-13 17:44:54 +0900550 * Checks the validity of activation flag.
551 *
552 * @param activationFlag activation flag
553 * @return returns true if the activation flag is valid, false otherwise
554 */
555 public static boolean checkActivationFlag(String activationFlag) {
556
557 switch (activationFlag) {
558 case ENABLE:
559 return true;
560 case DISABLE:
561 return false;
562 default:
563 throw new IllegalArgumentException("The given activation flag is not valid!");
564 }
565 }
566
567 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900568 * Swaps current location with old location info.
569 * The revised instance port will be used to mod the flow rules after migration.
570 *
571 * @param instPort instance port
572 * @return location swapped instance port
573 */
574 public static InstancePort swapStaleLocation(InstancePort instPort) {
575 return DefaultInstancePort.builder()
576 .deviceId(instPort.oldDeviceId())
577 .portNumber(instPort.oldPortNumber())
578 .state(instPort.state())
579 .ipAddress(instPort.ipAddress())
580 .macAddress(instPort.macAddress())
581 .networkId(instPort.networkId())
582 .portId(instPort.portId())
583 .build();
584 }
585
586 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900587 * Compares two router interfaces are equal.
588 * Will be remove this after Openstack4j implements equals.
589 *
590 * @param routerInterface1 router interface
591 * @param routerInterface2 router interface
592 * @return returns true if two router interfaces are equal, false otherwise
593 */
Jian Li63430202018-08-30 16:24:09 +0900594 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
595 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900596 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
597 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
598 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
599 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
600 }
601
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900602 /**
603 * Returns the vnic type of given port.
604 *
605 * @param portName port name
606 * @return vnit type
607 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900608 public static VnicType vnicType(String portName) {
609 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
610 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
611 return VnicType.NORMAL;
612 } else if (isDirectPort(portName)) {
613 return VnicType.DIRECT;
614 } else {
615 return VnicType.UNSUPPORTED;
616 }
617 }
618
Jian Li63430202018-08-30 16:24:09 +0900619 /**
620 * Deserializes raw payload into HttpRequest object.
621 *
622 * @param rawData raw http payload
623 * @return HttpRequest object
624 */
625 public static HttpRequest parseHttpRequest(byte[] rawData) {
626 SessionInputBufferImpl sessionInputBuffer =
627 new SessionInputBufferImpl(
628 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
629 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900630 DefaultHttpRequestParser requestParser =
631 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900632 try {
633 return requestParser.parse();
634 } catch (IOException | HttpException e) {
635 log.warn("Failed to parse HttpRequest, due to {}", e);
636 }
637
638 return null;
639 }
640
641 /**
642 * Serializes HttpRequest object to byte array.
643 *
644 * @param request http request object
645 * @return byte array
646 */
647 public static byte[] unparseHttpRequest(HttpRequest request) {
648 try {
649 SessionOutputBufferImpl sessionOutputBuffer =
650 new SessionOutputBufferImpl(
651 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
652
653 ByteArrayOutputStream baos = new ByteArrayOutputStream();
654 sessionOutputBuffer.bind(baos);
655
Jian Li5ecfd1a2018-12-10 11:41:03 +0900656 HttpMessageWriter<HttpRequest> requestWriter =
657 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900658 requestWriter.write(request);
659 sessionOutputBuffer.flush();
660
661 return baos.toByteArray();
662 } catch (HttpException | IOException e) {
663 log.warn("Failed to unparse HttpRequest, due to {}", e);
664 }
665
666 return null;
667 }
668
669 /**
670 * Deserializes raw payload into HttpResponse object.
671 *
672 * @param rawData raw http payload
673 * @return HttpResponse object
674 */
675 public static HttpResponse parseHttpResponse(byte[] rawData) {
676 SessionInputBufferImpl sessionInputBuffer =
677 new SessionInputBufferImpl(
678 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
679 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900680 DefaultHttpResponseParser responseParser =
681 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900682 try {
683 return responseParser.parse();
684 } catch (IOException | HttpException e) {
685 log.warn("Failed to parse HttpResponse, due to {}", e);
686 }
687
688 return null;
689 }
690
691 /**
692 * Serializes HttpResponse header to byte array.
693 *
694 * @param response http response object
695 * @return byte array
696 */
697 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
698 try {
699 SessionOutputBufferImpl sessionOutputBuffer =
700 new SessionOutputBufferImpl(
701 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
702
703 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
704 sessionOutputBuffer.bind(headerBaos);
705
706 HttpMessageWriter<HttpResponse> responseWriter =
707 new DefaultHttpResponseWriter(sessionOutputBuffer);
708 responseWriter.write(response);
709 sessionOutputBuffer.flush();
710
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900711 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900712
713 return headerBaos.toByteArray();
714 } catch (IOException | HttpException e) {
715 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
716 }
717
718 return null;
719 }
720
721 /**
722 * Serializes HttpResponse object to byte array.
723 *
724 * @param response http response object
725 * @return byte array
726 */
727 public static byte[] unparseHttpResponseBody(HttpResponse response) {
728 try {
729 ByteArrayOutputStream baos = new ByteArrayOutputStream();
730 response.getEntity().writeTo(baos);
731
732 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900733 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900734
735 return baos.toByteArray();
736 } catch (IOException e) {
737 log.warn("Failed to unparse HttpResponse, due to {}", e);
738 }
739
740 return null;
741 }
742
743 /**
744 * Encodes the given data using HmacSHA256 encryption method with given secret key.
745 *
746 * @param key secret key
747 * @param data data to be encrypted
748 * @return Hmac256 encrypted data
749 */
750 public static String hmacEncrypt(String key, String data) {
751 try {
752 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
753 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
754 sha256Hmac.init(secretKey);
755 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
756 } catch (Exception e) {
757 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
758 }
759 return null;
760 }
761
Daniel Parka73c2362018-09-17 17:43:25 +0900762 /**
763 * Creates flow trace request string.
764 *
765 * @param srcIp src ip address
766 * @param dstIp dst ip address
767 * @param srcInstancePort src instance port
768 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900769 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900770 * @return flow trace request string
771 */
772 public static String traceRequestString(String srcIp,
773 String dstIp,
774 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900775 OpenstackNetworkService osNetService,
776 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900777
778 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
779
780 if (uplink) {
781
782 requestStringBuilder.append(COMMA)
783 .append(IN_PORT)
784 .append(srcInstancePort.portNumber().toString())
785 .append(COMMA)
786 .append(NW_SRC)
787 .append(srcIp)
788 .append(COMMA);
789
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900790 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900791 Type netType = osNetService.networkType(srcInstancePort.networkId());
792 if (netType == Type.VXLAN || netType == Type.GRE ||
793 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900794 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900795 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900796 requestStringBuilder.append(DL_DST)
797 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900798 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
799 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900800 requestStringBuilder.append(DL_DST)
801 .append(DEFAULT_GATEWAY_MAC_STR)
802 .append(COMMA);
803 }
804 } else {
805 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900806 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900807 }
808 }
809
810 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900811 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900812 .append("\n");
813 } else {
814 requestStringBuilder.append(COMMA)
815 .append(NW_SRC)
816 .append(dstIp)
817 .append(COMMA);
818
Jian Li621f73c2018-12-15 01:49:22 +0900819 Type netType = osNetService.networkType(srcInstancePort.networkId());
820
821 if (netType == Type.VXLAN || netType == Type.GRE ||
822 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900823 requestStringBuilder.append(TUN_ID)
824 .append(osNetService.segmentId(srcInstancePort.networkId()))
825 .append(COMMA);
826 }
827 requestStringBuilder.append(NW_DST)
828 .append(srcIp)
829 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900830 }
831
832 return requestStringBuilder.toString();
833 }
834
835 /**
836 * Sends flow trace string to node.
837 *
838 * @param requestString reqeust string
839 * @param node src node
840 * @return flow trace result in string format
841 */
842 public static String sendTraceRequestToNode(String requestString,
843 OpenstackNode node) {
844 String traceResult = null;
845 OpenstackSshAuth sshAuth = node.sshAuthInfo();
846
847 try (SshClient client = SshClient.setUpDefaultClient()) {
848 client.start();
849
850 try (ClientSession session = client
851 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
852 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
853 session.addPasswordIdentity(sshAuth.password());
854 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
855
856
857 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
858
859 log.debug("requestString: {}", requestString);
860 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900861 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900862
Jian Li5ecfd1a2018-12-10 11:41:03 +0900863 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900864 OutputStream errStream = new ByteArrayOutputStream();
865
866 channel.setIn(new NoCloseInputStream(inputStream));
867 channel.setErr(errStream);
868 channel.setOut(outputStream);
869
870 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
871 eventList.add(ClientChannelEvent.OPENED);
872
873 OpenFuture channelFuture = channel.open();
874
875 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
876
877 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
878
879 while (!channelFuture.isOpened()) {
880 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
881 log.error("Failed to open channel");
882 return null;
883 }
884 }
885 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
886
Jian Li5ecfd1a2018-12-10 11:41:03 +0900887 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900888
889 channel.close();
890 }
891 } finally {
892 session.close();
893 }
894 } finally {
895 client.stop();
896 }
897
898 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900899 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900900 }
901
902 return traceResult;
903 }
904
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900905 /**
906 * Returns the floating ip with supplied instance port.
907 *
908 * @param instancePort instance port
909 * @param osRouterAdminService openstack router admin service
910 * @return floating ip
911 */
912 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900913 OpenstackRouterAdminService
914 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900915 return osRouterAdminService.floatingIps().stream()
916 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
917 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
918 .findAny().orElse(null);
919 }
920
921 /**
922 * Sends GARP packet with supplied floating ip information.
923 *
924 * @param floatingIP floating ip
925 * @param instancePort instance port
926 * @param vlanId vlain id
927 * @param gatewayNode gateway node
928 * @param packetService packet service
929 */
Jian Li32b03622018-11-06 17:54:24 +0900930 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
931 InstancePort instancePort,
932 VlanId vlanId,
933 OpenstackNode gatewayNode,
934 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900935 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
936
937 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
938 .setOutput(gatewayNode.uplinkPortNum()).build();
939
940 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
941 ByteBuffer.wrap(ethernet.serialize())));
942 }
943
944 /**
945 * Returns the external peer router with supplied network information.
946 *
947 * @param network network
948 * @param osNetworkService openstack network service
949 * @param osRouterAdminService openstack router admin service
950 * @return external peer router
951 */
952 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900953 OpenstackNetworkService
954 osNetworkService,
955 OpenstackRouterAdminService
956 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900957 if (network == null) {
958 return null;
959 }
960
Jian Lie6e609f2019-05-14 17:45:54 +0900961 Subnet subnet = osNetworkService.subnets(network.getId())
962 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900963
964 if (subnet == null) {
965 return null;
966 }
967
968 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
969 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
970 .findAny().orElse(null);
971 if (osRouterIface == null) {
972 return null;
973 }
974
975 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900976 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900977 return null;
978 }
979
980 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
981 return osNetworkService.externalPeerRouter(exGatewayInfo);
982
983 }
984
Jian Liebde74d2018-11-14 00:18:57 +0900985 /**
986 * Returns the external peer router with specified subnet information.
987 *
988 * @param subnet openstack subnet
989 * @param osRouterService openstack router service
990 * @param osNetworkService openstack network service
991 * @return external peer router
992 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900993 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
994 OpenstackRouterService
995 osRouterService,
996 OpenstackNetworkService
997 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900998 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
999 if (osRouter == null) {
1000 return null;
1001 }
1002 if (osRouter.getExternalGatewayInfo() == null) {
1003 // this router does not have external connectivity
1004 log.trace("router({}) has no external gateway",
1005 osRouter.getName());
1006 return null;
1007 }
1008
1009 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1010 }
1011
1012 /**
1013 * Returns the external ip address with specified router information.
1014 *
1015 * @param srcSubnet source subnet
1016 * @param osRouterService openstack router service
1017 * @param osNetworkService openstack network service
1018 * @return external ip address
1019 */
1020 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001021 OpenstackRouterService
1022 osRouterService,
1023 OpenstackNetworkService
1024 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001025
1026 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1027
1028 if (osRouter.getExternalGatewayInfo() == null) {
1029 // this router does not have external connectivity
1030 log.trace("router({}) has no external gateway",
1031 osRouter.getName());
1032 return null;
1033 }
1034
1035 return getExternalIp(osRouter, osNetworkService);
1036 }
1037
1038 /**
1039 * Returns the external ip address with specified router information.
1040 *
1041 * @param router openstack router
1042 * @param osNetworkService openstack network service
1043 * @return external ip address
1044 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001045 public static IpAddress getExternalIp(Router router,
1046 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001047 if (router == null) {
1048 return null;
1049 }
1050
1051 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1052 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001053 log.trace("Failed to get externalIp for router {} because " +
1054 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001055 router.getId());
1056 return null;
1057 }
1058
1059 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1060 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1061 .stream()
1062 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1063 .findAny().orElse(null);
1064
1065 if (exGatewayPort == null) {
1066 return null;
1067 }
1068
1069 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1070 .findAny().get().getIpAddress());
1071 }
1072
Jian Li2d68c192018-12-13 15:52:59 +09001073 /**
1074 * Returns the tunnel port number with specified net ID and openstack node.
1075 *
1076 * @param netId network ID
1077 * @param netService network service
1078 * @param osNode openstack node
1079 * @return tunnel port number
1080 */
1081 public static PortNumber tunnelPortNumByNetId(String netId,
1082 OpenstackNetworkService netService,
1083 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001084 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001085
1086 if (netType == null) {
1087 return null;
1088 }
1089
1090 return tunnelPortNumByNetType(netType, osNode);
1091 }
1092
1093 /**
1094 * Returns the tunnel port number with specified net type and openstack node.
1095 *
1096 * @param netType network type
1097 * @param osNode openstack node
1098 * @return tunnel port number
1099 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001100 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001101 switch (netType) {
1102 case VXLAN:
1103 return osNode.vxlanTunnelPortNum();
1104 case GRE:
1105 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001106 case GENEVE:
1107 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001108 default:
1109 return null;
1110 }
1111 }
1112
Jian Li7b8c3682019-05-12 13:57:15 +09001113 /**
1114 * Returns the REST URL of active node.
1115 *
1116 * @param haService openstack HA service
1117 * @return REST URL of active node
1118 */
1119 public static String getActiveUrl(OpenstackHaService haService) {
1120 return "http://" + haService.getActiveIp().toString() + ":" +
1121 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1122 }
1123
1124 /**
1125 * Returns the REST client instance with given resource path.
1126 *
1127 * @param haService openstack HA service
1128 * @param resourcePath resource path
1129 * @return REST client instance
1130 */
1131 public static WebTarget getActiveClient(OpenstackHaService haService,
1132 String resourcePath) {
1133 HttpAuthenticationFeature feature =
1134 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1135 Client client = ClientBuilder.newClient().register(feature);
1136 return client.target(getActiveUrl(haService)).path(resourcePath);
1137 }
1138
1139 /**
1140 * Returns the post response from the active node.
1141 *
1142 * @param haService openstack HA service
1143 * @param resourcePath resource path
1144 * @param input input
1145 * @return post response
1146 */
1147 public static Response syncPost(OpenstackHaService haService,
1148 String resourcePath,
1149 String input) {
1150
1151 log.debug("Sync POST request with {} on {}",
1152 haService.getActiveIp().toString(), resourcePath);
1153
1154 return getActiveClient(haService, resourcePath)
1155 .request(APPLICATION_JSON_TYPE)
1156 .post(Entity.json(input));
1157 }
1158
1159 /**
1160 * Returns the put response from the active node.
1161 *
1162 * @param haService openstack HA service
1163 * @param resourcePath resource path
1164 * @param id resource identifier
1165 * @param input input
1166 * @return put response
1167 */
1168 public static Response syncPut(OpenstackHaService haService,
1169 String resourcePath,
1170 String id, String input) {
1171 return syncPut(haService, resourcePath, null, id, input);
1172 }
1173
1174 /**
1175 * Returns the put response from the active node.
1176 *
1177 * @param haService openstack HA service
1178 * @param resourcePath resource path
1179 * @param id resource identifier
1180 * @param suffix resource suffix
1181 * @param input input
1182 * @return put response
1183 */
1184 public static Response syncPut(OpenstackHaService haService,
1185 String resourcePath,
1186 String suffix,
1187 String id, String input) {
1188
1189 log.debug("Sync PUT request with {} on {}",
1190 haService.getActiveIp().toString(), resourcePath);
1191
1192 String pathStr = "/" + id;
1193
1194 if (suffix != null) {
1195 pathStr += "/" + suffix;
1196 }
1197
1198 return getActiveClient(haService, resourcePath)
1199 .path(pathStr)
1200 .request(APPLICATION_JSON_TYPE)
1201 .put(Entity.json(input));
1202 }
1203
1204 /**
1205 * Returns the delete response from the active node.
1206 *
1207 * @param haService openstack HA service
1208 * @param resourcePath resource path
1209 * @param id resource identifier
1210 * @return delete response
1211 */
1212 public static Response syncDelete(OpenstackHaService haService,
1213 String resourcePath,
1214 String id) {
1215
1216 log.debug("Sync DELETE request with {} on {}",
1217 haService.getActiveIp().toString(), resourcePath);
1218
1219 return getActiveClient(haService, resourcePath)
1220 .path("/" + id)
1221 .request(APPLICATION_JSON_TYPE)
1222 .delete();
1223 }
1224
Jian Li51728702019-05-17 18:38:56 +09001225 /**
1226 * Gets the ovsdb client with supplied openstack node.
1227 *
1228 * @param node openstack node
1229 * @param ovsdbPort openvswitch DB port number
1230 * @param controller openvswitch DB controller instance
1231 * @return ovsdb client instance
1232 */
1233 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1234 OvsdbController controller) {
1235 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1236 return controller.getOvsdbClient(ovsdb);
1237 }
1238
1239 /**
1240 * Obtains the name of interface attached to the openstack VM.
1241 *
1242 * @param portId openstack port identifier
1243 * @return name of interface
1244 */
1245 public static String ifaceNameFromOsPortId(String portId) {
1246 if (portId != null) {
1247 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1248 }
1249
1250 return null;
1251 }
1252
Jian Li5ecfd1a2018-12-10 11:41:03 +09001253 private static Router getRouterFromSubnet(Subnet subnet,
1254 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001255 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1256 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1257 .findAny().orElse(null);
1258 if (osRouterIface == null) {
1259 return null;
1260 }
1261
1262 return osRouterService.router(osRouterIface.getId());
1263 }
1264
Daniel Park7e8c4d82018-08-13 23:47:49 +09001265 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001266 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001267 }
1268
Daniel Park2ff66b42018-08-01 11:52:45 +09001269 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001270 * Returns GARP packet with supplied floating ip and instance port information.
1271 *
1272 * @param floatingIP floating ip
1273 * @param instancePort instance port
1274 * @param vlanId vlan id
1275 * @return GARP packet
1276 */
1277 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1278 InstancePort instancePort,
1279 VlanId vlanId) {
1280 Ethernet ethernet = new Ethernet();
1281 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1282 ethernet.setSourceMACAddress(instancePort.macAddress());
1283 ethernet.setEtherType(Ethernet.TYPE_ARP);
1284 ethernet.setVlanID(vlanId.id());
1285
1286 ARP arp = new ARP();
1287 arp.setOpCode(ARP.OP_REPLY);
1288 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1289 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1290
1291 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1292 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1293
1294 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1295 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1296
Jian Li5ecfd1a2018-12-10 11:41:03 +09001297 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1298 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001299
1300 ethernet.setPayload(arp);
1301
1302 return ethernet;
1303 }
1304
1305 /**
Jian Li51b844c2018-05-31 10:59:03 +09001306 * Builds up and a complete endpoint URL from gateway node.
1307 *
1308 * @param node gateway node
1309 * @return a complete endpoint URL
1310 */
1311 private static String buildEndpoint(OpenstackNode node) {
1312
Jian Lic704b672018-09-04 18:52:53 +09001313 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001314
1315 StringBuilder endpointSb = new StringBuilder();
1316 endpointSb.append(auth.protocol().name().toLowerCase());
1317 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001318 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001319 return endpointSb.toString();
1320 }
1321
1322 /**
1323 * Obtains the SSL config without verifying the certification.
1324 *
1325 * @return SSL config
1326 */
1327 private static Config getSslConfig() {
1328 // we bypass the SSL certification verification for now
1329 // TODO: verify server side SSL using a given certification
1330 Config config = Config.newConfig().withSSLVerificationDisabled();
1331
1332 TrustManager[] trustAllCerts = new TrustManager[]{
1333 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001334 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001335 public X509Certificate[] getAcceptedIssuers() {
1336 return null;
1337 }
1338
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001339 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001340 public void checkClientTrusted(X509Certificate[] certs,
1341 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001342 return;
Jian Li51b844c2018-05-31 10:59:03 +09001343 }
1344
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001345 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001346 public void checkServerTrusted(X509Certificate[] certs,
1347 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001348 return;
Jian Li51b844c2018-05-31 10:59:03 +09001349 }
1350 }
1351 };
1352
1353 HostnameVerifier allHostsValid = (hostname, session) -> true;
1354
1355 try {
1356 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1357 sc.init(null, trustAllCerts,
1358 new java.security.SecureRandom());
1359 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1360 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1361
1362 config.withSSLContext(sc);
1363 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001364 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001365 return null;
1366 }
1367
1368 return config;
1369 }
1370
1371 /**
1372 * Obtains the facing object with given openstack perspective.
1373 *
1374 * @param perspective keystone perspective
1375 * @return facing object
1376 */
1377 private static Facing getFacing(Perspective perspective) {
1378
1379 switch (perspective) {
1380 case PUBLIC:
1381 return Facing.PUBLIC;
1382 case ADMIN:
1383 return Facing.ADMIN;
1384 case INTERNAL:
1385 return Facing.INTERNAL;
1386 default:
1387 return null;
1388 }
1389 }
1390
1391 /**
1392 * Obtains gateway instance by giving index number.
1393 *
1394 * @param gws a collection of gateway nodes
1395 * @param index index number
1396 * @return gateway instance
1397 */
1398 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1399 Map<String, OpenstackNode> hashMap = new HashMap<>();
1400 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1401 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1402 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1403
1404 int intIndex = 0;
1405 OpenstackNode gw = null;
1406 while (iteratorKey.hasNext()) {
1407 String key = iteratorKey.next();
1408
1409 if (intIndex == index) {
1410 gw = treeMap.get(key);
1411 }
1412 intIndex++;
1413 }
1414 return gw;
1415 }
Jian Li63430202018-08-30 16:24:09 +09001416}