blob: 31882c972f5fcb6374780ac0ce551d6288785e37 [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;
28import org.apache.http.HttpException;
29import org.apache.http.HttpRequest;
30import org.apache.http.HttpResponse;
31import org.apache.http.impl.io.DefaultHttpRequestParser;
32import org.apache.http.impl.io.DefaultHttpRequestWriter;
33import org.apache.http.impl.io.DefaultHttpResponseParser;
34import org.apache.http.impl.io.DefaultHttpResponseWriter;
35import org.apache.http.impl.io.HttpTransportMetricsImpl;
36import org.apache.http.impl.io.SessionInputBufferImpl;
37import org.apache.http.impl.io.SessionOutputBufferImpl;
38import org.apache.http.io.HttpMessageWriter;
Daniel Parka73c2362018-09-17 17:43:25 +090039import org.apache.sshd.client.SshClient;
40import org.apache.sshd.client.channel.ClientChannel;
41import org.apache.sshd.client.channel.ClientChannelEvent;
42import org.apache.sshd.client.future.OpenFuture;
43import org.apache.sshd.client.session.ClientSession;
44import org.apache.sshd.common.util.io.NoCloseInputStream;
Jian Li7b8c3682019-05-12 13:57:15 +090045import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090046import org.onlab.packet.ARP;
47import org.onlab.packet.Ethernet;
48import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090049import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090050import org.onlab.packet.MacAddress;
51import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090052import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090053import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090054import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090055import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090056import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.TrafficTreatment;
58import org.onosproject.net.packet.DefaultOutboundPacket;
59import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090060import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090061import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090062import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li7b8c3682019-05-12 13:57:15 +090063import org.onosproject.openstacknetworking.api.OpenstackHaService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090064import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090065import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090066import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090067import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090068import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090069import org.onosproject.openstacknode.api.OpenstackAuth;
70import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090071import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090072import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51b844c2018-05-31 10:59:03 +090073import org.openstack4j.api.OSClient;
74import org.openstack4j.api.client.IOSClientBuilder;
75import org.openstack4j.api.exceptions.AuthenticationException;
76import org.openstack4j.api.types.Facing;
77import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090078import org.openstack4j.core.transport.ObjectMapperSingleton;
79import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090080import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090081import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090082import org.openstack4j.model.network.NetFloatingIP;
83import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090084import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090085import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090086import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090087import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090088import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090089import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090090import org.slf4j.Logger;
91import org.slf4j.LoggerFactory;
92
Jian Li63430202018-08-30 16:24:09 +090093import javax.crypto.Mac;
94import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090095import javax.net.ssl.HostnameVerifier;
96import javax.net.ssl.HttpsURLConnection;
97import javax.net.ssl.SSLContext;
98import javax.net.ssl.TrustManager;
99import javax.net.ssl.X509TrustManager;
Jian Li7b8c3682019-05-12 13:57:15 +0900100import javax.ws.rs.client.Client;
101import javax.ws.rs.client.ClientBuilder;
102import javax.ws.rs.client.Entity;
103import javax.ws.rs.client.WebTarget;
104import javax.ws.rs.core.Response;
Jian Li63430202018-08-30 16:24:09 +0900105import java.io.ByteArrayInputStream;
106import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900107import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900108import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900109import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900110import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900111import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900112import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900113import java.util.HashMap;
114import java.util.Iterator;
115import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900116import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900117import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900118import java.util.Set;
119import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900120import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900121
122import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900123import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900124import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li7b8c3682019-05-12 13:57:15 +0900125import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
126import static org.apache.commons.io.IOUtils.toInputStream;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900127import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900128import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900129import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li7b8c3682019-05-12 13:57:15 +0900130import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_REST_PATH;
Daniel Parkc4d06402018-05-28 15:57:37 +0900131import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
132import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900133import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
134import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Jian Li7b8c3682019-05-12 13:57:15 +0900135import static org.onosproject.openstacknetworking.api.Constants.REST_PASSWORD;
136import static org.onosproject.openstacknetworking.api.Constants.REST_PORT;
137import static org.onosproject.openstacknetworking.api.Constants.REST_USER;
138import static org.onosproject.openstacknetworking.api.Constants.REST_UTF8;
Daniel Parkec9d1132018-08-19 11:18:03 +0900139import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700140import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900141import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900142
143/**
144 * An utility that used in openstack networking app.
145 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900146public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900147
Daniel Park95985382018-07-23 11:38:07 +0900148 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900149
Daniel Parkc4d06402018-05-28 15:57:37 +0900150 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900151 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900152 private static final String PREFIX_DEVICE_NUMBER = "s";
153 private static final String PREFIX_FUNCTION_NUMBER = "f";
154
Jian Li51b844c2018-05-31 10:59:03 +0900155 // keystone endpoint related variables
156 private static final String DOMAIN_DEFAULT = "default";
157 private static final String KEYSTONE_V2 = "v2.0";
158 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900159 private static final String SSL_TYPE = "SSL";
160
Jian Li7f024de2018-07-07 03:51:02 +0900161 private static final String PROXY_MODE = "proxy";
162 private static final String BROADCAST_MODE = "broadcast";
163
Jian Licad36c72018-09-13 17:44:54 +0900164 private static final String ENABLE = "enable";
165 private static final String DISABLE = "disable";
166
Jian Li63430202018-08-30 16:24:09 +0900167 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
168
169 private static final String HMAC_SHA256 = "HmacSHA256";
170
Jian Li24ec59f2018-05-23 19:01:25 +0900171 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
172
Daniel Parka73c2362018-09-17 17:43:25 +0900173 private static final String DL_DST = "dl_dst=";
174 private static final String NW_DST = "nw_dst=";
175 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
176 private static final String IN_PORT = "in_port=";
177 private static final String NW_SRC = "nw_src=";
178 private static final String COMMA = ",";
179 private static final String TUN_ID = "tun_id=";
180
181 private static final long TIMEOUT_MS = 5000;
182 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
183 private static final int SSH_PORT = 22;
184
Jian Li091d8d22018-02-20 10:42:06 +0900185 /**
186 * Prevents object instantiation from external.
187 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900188 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900189 }
190
191 /**
192 * Interprets JSON string to corresponding openstack model entity object.
193 *
Jian Li7b8c3682019-05-12 13:57:15 +0900194 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900195 * @param entityClazz openstack model entity class
196 * @return openstack model entity object
197 */
Jian Li7b8c3682019-05-12 13:57:15 +0900198 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900199 ObjectMapper mapper = new ObjectMapper();
200 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900201 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900202 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
203 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
204 return ObjectMapperSingleton.getContext(entityClazz)
205 .readerFor(entityClazz)
206 .readValue(jsonTree);
207 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900208 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900209 throw new IllegalArgumentException();
210 }
211 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900212
213 /**
214 * Converts openstack model entity object into JSON object.
215 *
216 * @param entity openstack model entity object
217 * @param entityClazz openstack model entity class
218 * @return JSON object
219 */
220 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
221 ObjectMapper mapper = new ObjectMapper();
222 try {
223 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
224 .writerFor(entityClazz)
225 .writeValueAsString(entity);
226 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900227 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900228 } catch (IOException e) {
229 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900230 throw new IllegalStateException();
231 }
232 }
Jian Li1064e4f2018-05-29 16:16:53 +0900233
234 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900235 * Obtains a floating IP associated with the given instance port.
236 *
237 * @param port instance port
238 * @param fips a collection of floating IPs
239 * @return associated floating IP
240 */
241 public static NetFloatingIP associatedFloatingIp(InstancePort port,
242 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900243 for (NetFloatingIP fip : fips) {
244 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
245 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900246 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900247 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
248 continue;
249 }
Jian Li6bc29d92018-10-02 13:55:05 +0900250 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
251 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900252 return fip;
253 }
Jian Li24ec59f2018-05-23 19:01:25 +0900254 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900255
Jian Li24ec59f2018-05-23 19:01:25 +0900256 return null;
257 }
258
259 /**
260 * Checks whether the given floating IP is associated with a VM.
261 *
262 * @param service openstack network service
263 * @param fip floating IP
264 * @return true if the given floating IP associated with a VM, false otherwise
265 */
266 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
267 NetFloatingIP fip) {
268 Port osPort = service.port(fip.getPortId());
269 if (osPort == null) {
270 return false;
271 }
272
273 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
274 Network osNet = service.network(osPort.getNetworkId());
275 if (osNet == null) {
276 final String errorFormat = ERR_FLOW + "no network(%s) exists";
277 final String error = String.format(errorFormat,
278 fip.getFloatingIpAddress(), osPort.getNetworkId());
279 throw new IllegalStateException(error);
280 }
281 return true;
282 } else {
283 return false;
284 }
285 }
286
287 /**
Jian Lia171a432018-06-11 11:52:11 +0900288 * Obtains the gateway node by instance port.
289 *
290 * @param gateways a collection of gateway nodes
291 * @param instPort instance port
292 * @return a gateway node
293 */
294 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
295 InstancePort instPort) {
296 OpenstackNode gw = null;
297 if (instPort != null && instPort.deviceId() != null) {
298 gw = getGwByComputeDevId(gateways, instPort.deviceId());
299 }
300 return gw;
301 }
302
303 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900304 * Obtains the gateway node by device in compute node. Note that the gateway
305 * node is determined by device's device identifier.
306 *
307 * @param gws a collection of gateway nodes
308 * @param deviceId device identifier
309 * @return a gateway node
310 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900311 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
312 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900313 int numOfGw = gws.size();
314
315 if (numOfGw == 0) {
316 return null;
317 }
318
319 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
320
321 return getGwByIndex(gws, gwIndex);
322 }
323
Jian Li51b844c2018-05-31 10:59:03 +0900324 /**
325 * Obtains a connected openstack client.
326 *
327 * @param osNode openstack node
328 * @return a connected openstack client
329 */
330 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900331 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900332 String endpoint = buildEndpoint(osNode);
333 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900334
Jian Li51b844c2018-05-31 10:59:03 +0900335 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900336
Jian Li51b844c2018-05-31 10:59:03 +0900337 try {
338 if (endpoint.contains(KEYSTONE_V2)) {
339 IOSClientBuilder.V2 builder = OSFactory.builderV2()
340 .endpoint(endpoint)
341 .tenantName(auth.project())
342 .credentials(auth.username(), auth.password())
343 .withConfig(config);
344
345 if (perspective != null) {
346 builder.perspective(getFacing(perspective));
347 }
348
349 return builder.authenticate();
350 } else if (endpoint.contains(KEYSTONE_V3)) {
351
352 Identifier project = Identifier.byName(auth.project());
353 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
354
355 IOSClientBuilder.V3 builder = OSFactory.builderV3()
356 .endpoint(endpoint)
357 .credentials(auth.username(), auth.password(), domain)
358 .scopeToProject(project, domain)
359 .withConfig(config);
360
361 if (perspective != null) {
362 builder.perspective(getFacing(perspective));
363 }
364
365 return builder.authenticate();
366 } else {
367 log.warn("Unrecognized keystone version type");
368 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900369 }
Jian Li51b844c2018-05-31 10:59:03 +0900370 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900371 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900372 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900373 }
Jian Li1064e4f2018-05-29 16:16:53 +0900374 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900375
376 /**
377 * Extract the interface name with the supplied port.
378 *
379 * @param port port
380 * @return interface name
381 */
382 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900383 String intfName;
384
Daniel Park95985382018-07-23 11:38:07 +0900385 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900386 log.error("Port profile is not found");
387 return null;
388 }
389
Daniel Park95985382018-07-23 11:38:07 +0900390 if (!port.getProfile().containsKey(PCISLOT) ||
391 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900392 log.error("Failed to retrieve the interface name because of no " +
393 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900394 return null;
395 }
Jian Li51b844c2018-05-31 10:59:03 +0900396
Daniel Parkff178ba2018-11-23 15:57:24 +0900397 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
398
399 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
400 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
401 return UNSUPPORTED_VENDOR;
402 }
403
404 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
405
Daniel Parkc4d06402018-05-28 15:57:37 +0900406 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
407 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
408
409 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
410 .split(":")[2]
411 .split("\\.")[0];
412 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
413
414 String functionNumHex = port.getProfile().get(PCISLOT).toString()
415 .split(":")[2]
416 .split("\\.")[1];
417 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
418
Daniel Parkc4d06402018-05-28 15:57:37 +0900419 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
420 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
421 } else {
422 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
423 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
424 }
425
426 return intfName;
427 }
Jian Li51b844c2018-05-31 10:59:03 +0900428
429 /**
Daniel Park95f73312018-07-31 15:48:34 +0900430 * Check if the given interface is added to the given device or not.
431 *
432 * @param deviceId device ID
433 * @param intfName interface name
434 * @param deviceService device service
435 * @return true if the given interface is added to the given device or false otherwise
436 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900437 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
438 String intfName,
439 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900440 checkNotNull(deviceId);
441 checkNotNull(intfName);
442
Jian Li5ecfd1a2018-12-10 11:41:03 +0900443 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
444 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900445 }
446
447 /**
Jian Li0b564282018-06-20 00:50:53 +0900448 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900449 *
450 * @param osPort port
451 * @param adminService openstack admin service
452 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900453 public static void addRouterIface(Port osPort,
454 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900455 osPort.getFixedIps().forEach(p -> {
456 JsonNode jsonTree = new ObjectMapper().createObjectNode()
457 .put("id", osPort.getDeviceId())
458 .put("tenant_id", osPort.getTenantId())
459 .put("subnet_id", p.getSubnetId())
460 .put("port_id", osPort.getId());
461 try {
462 RouterInterface rIface = getContext(NeutronRouterInterface.class)
463 .readerFor(NeutronRouterInterface.class)
464 .readValue(jsonTree);
465 if (adminService.routerInterface(rIface.getPortId()) != null) {
466 adminService.updateRouterInterface(rIface);
467 } else {
468 adminService.addRouterInterface(rIface);
469 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900470 } catch (IOException e) {
471 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900472 }
473 });
474 }
475
476 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900477 * Obtains the property value with specified property key name.
478 *
479 * @param properties a collection of properties
480 * @param name key name
481 * @return mapping value
482 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900483 public static String getPropertyValue(Set<ConfigProperty> properties,
484 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900485 Optional<ConfigProperty> property =
486 properties.stream().filter(p -> p.name().equals(name)).findFirst();
487 return property.map(ConfigProperty::value).orElse(null);
488 }
489
490 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900491 * Obtains the boolean property value with specified property key name.
492 *
493 * @param properties a collection of properties
494 * @param name key name
495 * @return mapping value
496 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900497 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
498 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900499 Optional<ConfigProperty> property =
500 properties.stream().filter(p -> p.name().equals(name)).findFirst();
501
502 return property.map(ConfigProperty::asBoolean).orElse(false);
503 }
504
505 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900506 * Prints out the JSON string in pretty format.
507 *
508 * @param mapper Object mapper
509 * @param jsonString JSON string
510 * @return pretty formatted JSON string
511 */
512 public static String prettyJson(ObjectMapper mapper, String jsonString) {
513 try {
514 Object jsonObject = mapper.readValue(jsonString, Object.class);
515 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900516 } catch (JsonParseException e) {
517 log.debug("JsonParseException caused by {}", e);
518 } catch (JsonMappingException e) {
519 log.debug("JsonMappingException caused by {}", e);
520 } catch (JsonProcessingException e) {
521 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900522 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900523 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900524 }
525 return null;
526 }
527
528 /**
Jian Li7f024de2018-07-07 03:51:02 +0900529 * Checks the validity of ARP mode.
530 *
531 * @param arpMode ARP mode
532 * @return returns true if the ARP mode is valid, false otherwise
533 */
534 public static boolean checkArpMode(String arpMode) {
535
536 if (isNullOrEmpty(arpMode)) {
537 return false;
538 } else {
539 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
540 }
541 }
542
543 /**
Jian Licad36c72018-09-13 17:44:54 +0900544 * Checks the validity of activation flag.
545 *
546 * @param activationFlag activation flag
547 * @return returns true if the activation flag is valid, false otherwise
548 */
549 public static boolean checkActivationFlag(String activationFlag) {
550
551 switch (activationFlag) {
552 case ENABLE:
553 return true;
554 case DISABLE:
555 return false;
556 default:
557 throw new IllegalArgumentException("The given activation flag is not valid!");
558 }
559 }
560
561 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900562 * Swaps current location with old location info.
563 * The revised instance port will be used to mod the flow rules after migration.
564 *
565 * @param instPort instance port
566 * @return location swapped instance port
567 */
568 public static InstancePort swapStaleLocation(InstancePort instPort) {
569 return DefaultInstancePort.builder()
570 .deviceId(instPort.oldDeviceId())
571 .portNumber(instPort.oldPortNumber())
572 .state(instPort.state())
573 .ipAddress(instPort.ipAddress())
574 .macAddress(instPort.macAddress())
575 .networkId(instPort.networkId())
576 .portId(instPort.portId())
577 .build();
578 }
579
580 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900581 * Compares two router interfaces are equal.
582 * Will be remove this after Openstack4j implements equals.
583 *
584 * @param routerInterface1 router interface
585 * @param routerInterface2 router interface
586 * @return returns true if two router interfaces are equal, false otherwise
587 */
Jian Li63430202018-08-30 16:24:09 +0900588 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
589 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900590 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
591 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
592 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
593 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
594 }
595
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900596 /**
597 * Returns the vnic type of given port.
598 *
599 * @param portName port name
600 * @return vnit type
601 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900602 public static VnicType vnicType(String portName) {
603 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
604 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
605 return VnicType.NORMAL;
606 } else if (isDirectPort(portName)) {
607 return VnicType.DIRECT;
608 } else {
609 return VnicType.UNSUPPORTED;
610 }
611 }
612
Jian Li63430202018-08-30 16:24:09 +0900613 /**
614 * Deserializes raw payload into HttpRequest object.
615 *
616 * @param rawData raw http payload
617 * @return HttpRequest object
618 */
619 public static HttpRequest parseHttpRequest(byte[] rawData) {
620 SessionInputBufferImpl sessionInputBuffer =
621 new SessionInputBufferImpl(
622 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
623 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900624 DefaultHttpRequestParser requestParser =
625 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900626 try {
627 return requestParser.parse();
628 } catch (IOException | HttpException e) {
629 log.warn("Failed to parse HttpRequest, due to {}", e);
630 }
631
632 return null;
633 }
634
635 /**
636 * Serializes HttpRequest object to byte array.
637 *
638 * @param request http request object
639 * @return byte array
640 */
641 public static byte[] unparseHttpRequest(HttpRequest request) {
642 try {
643 SessionOutputBufferImpl sessionOutputBuffer =
644 new SessionOutputBufferImpl(
645 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
646
647 ByteArrayOutputStream baos = new ByteArrayOutputStream();
648 sessionOutputBuffer.bind(baos);
649
Jian Li5ecfd1a2018-12-10 11:41:03 +0900650 HttpMessageWriter<HttpRequest> requestWriter =
651 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900652 requestWriter.write(request);
653 sessionOutputBuffer.flush();
654
655 return baos.toByteArray();
656 } catch (HttpException | IOException e) {
657 log.warn("Failed to unparse HttpRequest, due to {}", e);
658 }
659
660 return null;
661 }
662
663 /**
664 * Deserializes raw payload into HttpResponse object.
665 *
666 * @param rawData raw http payload
667 * @return HttpResponse object
668 */
669 public static HttpResponse parseHttpResponse(byte[] rawData) {
670 SessionInputBufferImpl sessionInputBuffer =
671 new SessionInputBufferImpl(
672 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
673 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900674 DefaultHttpResponseParser responseParser =
675 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900676 try {
677 return responseParser.parse();
678 } catch (IOException | HttpException e) {
679 log.warn("Failed to parse HttpResponse, due to {}", e);
680 }
681
682 return null;
683 }
684
685 /**
686 * Serializes HttpResponse header to byte array.
687 *
688 * @param response http response object
689 * @return byte array
690 */
691 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
692 try {
693 SessionOutputBufferImpl sessionOutputBuffer =
694 new SessionOutputBufferImpl(
695 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
696
697 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
698 sessionOutputBuffer.bind(headerBaos);
699
700 HttpMessageWriter<HttpResponse> responseWriter =
701 new DefaultHttpResponseWriter(sessionOutputBuffer);
702 responseWriter.write(response);
703 sessionOutputBuffer.flush();
704
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900705 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900706
707 return headerBaos.toByteArray();
708 } catch (IOException | HttpException e) {
709 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
710 }
711
712 return null;
713 }
714
715 /**
716 * Serializes HttpResponse object to byte array.
717 *
718 * @param response http response object
719 * @return byte array
720 */
721 public static byte[] unparseHttpResponseBody(HttpResponse response) {
722 try {
723 ByteArrayOutputStream baos = new ByteArrayOutputStream();
724 response.getEntity().writeTo(baos);
725
726 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900727 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900728
729 return baos.toByteArray();
730 } catch (IOException e) {
731 log.warn("Failed to unparse HttpResponse, due to {}", e);
732 }
733
734 return null;
735 }
736
737 /**
738 * Encodes the given data using HmacSHA256 encryption method with given secret key.
739 *
740 * @param key secret key
741 * @param data data to be encrypted
742 * @return Hmac256 encrypted data
743 */
744 public static String hmacEncrypt(String key, String data) {
745 try {
746 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
747 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
748 sha256Hmac.init(secretKey);
749 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
750 } catch (Exception e) {
751 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
752 }
753 return null;
754 }
755
Daniel Parka73c2362018-09-17 17:43:25 +0900756 /**
757 * Creates flow trace request string.
758 *
759 * @param srcIp src ip address
760 * @param dstIp dst ip address
761 * @param srcInstancePort src instance port
762 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900763 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900764 * @return flow trace request string
765 */
766 public static String traceRequestString(String srcIp,
767 String dstIp,
768 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900769 OpenstackNetworkService osNetService,
770 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900771
772 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
773
774 if (uplink) {
775
776 requestStringBuilder.append(COMMA)
777 .append(IN_PORT)
778 .append(srcInstancePort.portNumber().toString())
779 .append(COMMA)
780 .append(NW_SRC)
781 .append(srcIp)
782 .append(COMMA);
783
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900784 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900785 Type netType = osNetService.networkType(srcInstancePort.networkId());
786 if (netType == Type.VXLAN || netType == Type.GRE ||
787 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900788 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900789 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900790 requestStringBuilder.append(DL_DST)
791 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900792 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
793 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900794 requestStringBuilder.append(DL_DST)
795 .append(DEFAULT_GATEWAY_MAC_STR)
796 .append(COMMA);
797 }
798 } else {
799 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900800 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900801 }
802 }
803
804 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900805 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900806 .append("\n");
807 } else {
808 requestStringBuilder.append(COMMA)
809 .append(NW_SRC)
810 .append(dstIp)
811 .append(COMMA);
812
Jian Li621f73c2018-12-15 01:49:22 +0900813 Type netType = osNetService.networkType(srcInstancePort.networkId());
814
815 if (netType == Type.VXLAN || netType == Type.GRE ||
816 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900817 requestStringBuilder.append(TUN_ID)
818 .append(osNetService.segmentId(srcInstancePort.networkId()))
819 .append(COMMA);
820 }
821 requestStringBuilder.append(NW_DST)
822 .append(srcIp)
823 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900824 }
825
826 return requestStringBuilder.toString();
827 }
828
829 /**
830 * Sends flow trace string to node.
831 *
832 * @param requestString reqeust string
833 * @param node src node
834 * @return flow trace result in string format
835 */
836 public static String sendTraceRequestToNode(String requestString,
837 OpenstackNode node) {
838 String traceResult = null;
839 OpenstackSshAuth sshAuth = node.sshAuthInfo();
840
841 try (SshClient client = SshClient.setUpDefaultClient()) {
842 client.start();
843
844 try (ClientSession session = client
845 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
846 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
847 session.addPasswordIdentity(sshAuth.password());
848 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
849
850
851 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
852
853 log.debug("requestString: {}", requestString);
854 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900855 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900856
Jian Li5ecfd1a2018-12-10 11:41:03 +0900857 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900858 OutputStream errStream = new ByteArrayOutputStream();
859
860 channel.setIn(new NoCloseInputStream(inputStream));
861 channel.setErr(errStream);
862 channel.setOut(outputStream);
863
864 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
865 eventList.add(ClientChannelEvent.OPENED);
866
867 OpenFuture channelFuture = channel.open();
868
869 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
870
871 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
872
873 while (!channelFuture.isOpened()) {
874 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
875 log.error("Failed to open channel");
876 return null;
877 }
878 }
879 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
880
Jian Li5ecfd1a2018-12-10 11:41:03 +0900881 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900882
883 channel.close();
884 }
885 } finally {
886 session.close();
887 }
888 } finally {
889 client.stop();
890 }
891
892 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900893 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900894 }
895
896 return traceResult;
897 }
898
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900899 /**
900 * Returns the floating ip with supplied instance port.
901 *
902 * @param instancePort instance port
903 * @param osRouterAdminService openstack router admin service
904 * @return floating ip
905 */
906 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900907 OpenstackRouterAdminService
908 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900909 return osRouterAdminService.floatingIps().stream()
910 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
911 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
912 .findAny().orElse(null);
913 }
914
915 /**
916 * Sends GARP packet with supplied floating ip information.
917 *
918 * @param floatingIP floating ip
919 * @param instancePort instance port
920 * @param vlanId vlain id
921 * @param gatewayNode gateway node
922 * @param packetService packet service
923 */
Jian Li32b03622018-11-06 17:54:24 +0900924 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
925 InstancePort instancePort,
926 VlanId vlanId,
927 OpenstackNode gatewayNode,
928 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900929 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
930
931 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
932 .setOutput(gatewayNode.uplinkPortNum()).build();
933
934 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
935 ByteBuffer.wrap(ethernet.serialize())));
936 }
937
938 /**
939 * Returns the external peer router with supplied network information.
940 *
941 * @param network network
942 * @param osNetworkService openstack network service
943 * @param osRouterAdminService openstack router admin service
944 * @return external peer router
945 */
946 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900947 OpenstackNetworkService
948 osNetworkService,
949 OpenstackRouterAdminService
950 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900951 if (network == null) {
952 return null;
953 }
954
955 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
956
957 if (subnet == null) {
958 return null;
959 }
960
961 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
962 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
963 .findAny().orElse(null);
964 if (osRouterIface == null) {
965 return null;
966 }
967
968 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900969 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900970 return null;
971 }
972
973 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
974 return osNetworkService.externalPeerRouter(exGatewayInfo);
975
976 }
977
Jian Liebde74d2018-11-14 00:18:57 +0900978 /**
979 * Returns the external peer router with specified subnet information.
980 *
981 * @param subnet openstack subnet
982 * @param osRouterService openstack router service
983 * @param osNetworkService openstack network service
984 * @return external peer router
985 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900986 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
987 OpenstackRouterService
988 osRouterService,
989 OpenstackNetworkService
990 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900991 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
992 if (osRouter == null) {
993 return null;
994 }
995 if (osRouter.getExternalGatewayInfo() == null) {
996 // this router does not have external connectivity
997 log.trace("router({}) has no external gateway",
998 osRouter.getName());
999 return null;
1000 }
1001
1002 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1003 }
1004
1005 /**
1006 * Returns the external ip address with specified router information.
1007 *
1008 * @param srcSubnet source subnet
1009 * @param osRouterService openstack router service
1010 * @param osNetworkService openstack network service
1011 * @return external ip address
1012 */
1013 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001014 OpenstackRouterService
1015 osRouterService,
1016 OpenstackNetworkService
1017 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001018
1019 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1020
1021 if (osRouter.getExternalGatewayInfo() == null) {
1022 // this router does not have external connectivity
1023 log.trace("router({}) has no external gateway",
1024 osRouter.getName());
1025 return null;
1026 }
1027
1028 return getExternalIp(osRouter, osNetworkService);
1029 }
1030
1031 /**
1032 * Returns the external ip address with specified router information.
1033 *
1034 * @param router openstack router
1035 * @param osNetworkService openstack network service
1036 * @return external ip address
1037 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001038 public static IpAddress getExternalIp(Router router,
1039 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001040 if (router == null) {
1041 return null;
1042 }
1043
1044 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1045 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001046 log.trace("Failed to get externalIp for router {} because " +
1047 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001048 router.getId());
1049 return null;
1050 }
1051
1052 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1053 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1054 .stream()
1055 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1056 .findAny().orElse(null);
1057
1058 if (exGatewayPort == null) {
1059 return null;
1060 }
1061
1062 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1063 .findAny().get().getIpAddress());
1064 }
1065
Jian Li2d68c192018-12-13 15:52:59 +09001066 /**
1067 * Returns the tunnel port number with specified net ID and openstack node.
1068 *
1069 * @param netId network ID
1070 * @param netService network service
1071 * @param osNode openstack node
1072 * @return tunnel port number
1073 */
1074 public static PortNumber tunnelPortNumByNetId(String netId,
1075 OpenstackNetworkService netService,
1076 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001077 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001078
1079 if (netType == null) {
1080 return null;
1081 }
1082
1083 return tunnelPortNumByNetType(netType, osNode);
1084 }
1085
1086 /**
1087 * Returns the tunnel port number with specified net type and openstack node.
1088 *
1089 * @param netType network type
1090 * @param osNode openstack node
1091 * @return tunnel port number
1092 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001093 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001094 switch (netType) {
1095 case VXLAN:
1096 return osNode.vxlanTunnelPortNum();
1097 case GRE:
1098 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001099 case GENEVE:
1100 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001101 default:
1102 return null;
1103 }
1104 }
1105
Jian Li7b8c3682019-05-12 13:57:15 +09001106 /**
1107 * Returns the REST URL of active node.
1108 *
1109 * @param haService openstack HA service
1110 * @return REST URL of active node
1111 */
1112 public static String getActiveUrl(OpenstackHaService haService) {
1113 return "http://" + haService.getActiveIp().toString() + ":" +
1114 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1115 }
1116
1117 /**
1118 * Returns the REST client instance with given resource path.
1119 *
1120 * @param haService openstack HA service
1121 * @param resourcePath resource path
1122 * @return REST client instance
1123 */
1124 public static WebTarget getActiveClient(OpenstackHaService haService,
1125 String resourcePath) {
1126 HttpAuthenticationFeature feature =
1127 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1128 Client client = ClientBuilder.newClient().register(feature);
1129 return client.target(getActiveUrl(haService)).path(resourcePath);
1130 }
1131
1132 /**
1133 * Returns the post response from the active node.
1134 *
1135 * @param haService openstack HA service
1136 * @param resourcePath resource path
1137 * @param input input
1138 * @return post response
1139 */
1140 public static Response syncPost(OpenstackHaService haService,
1141 String resourcePath,
1142 String input) {
1143
1144 log.debug("Sync POST request with {} on {}",
1145 haService.getActiveIp().toString(), resourcePath);
1146
1147 return getActiveClient(haService, resourcePath)
1148 .request(APPLICATION_JSON_TYPE)
1149 .post(Entity.json(input));
1150 }
1151
1152 /**
1153 * Returns the put response from the active node.
1154 *
1155 * @param haService openstack HA service
1156 * @param resourcePath resource path
1157 * @param id resource identifier
1158 * @param input input
1159 * @return put response
1160 */
1161 public static Response syncPut(OpenstackHaService haService,
1162 String resourcePath,
1163 String id, String input) {
1164 return syncPut(haService, resourcePath, null, id, input);
1165 }
1166
1167 /**
1168 * Returns the put response from the active node.
1169 *
1170 * @param haService openstack HA service
1171 * @param resourcePath resource path
1172 * @param id resource identifier
1173 * @param suffix resource suffix
1174 * @param input input
1175 * @return put response
1176 */
1177 public static Response syncPut(OpenstackHaService haService,
1178 String resourcePath,
1179 String suffix,
1180 String id, String input) {
1181
1182 log.debug("Sync PUT request with {} on {}",
1183 haService.getActiveIp().toString(), resourcePath);
1184
1185 String pathStr = "/" + id;
1186
1187 if (suffix != null) {
1188 pathStr += "/" + suffix;
1189 }
1190
1191 return getActiveClient(haService, resourcePath)
1192 .path(pathStr)
1193 .request(APPLICATION_JSON_TYPE)
1194 .put(Entity.json(input));
1195 }
1196
1197 /**
1198 * Returns the delete response from the active node.
1199 *
1200 * @param haService openstack HA service
1201 * @param resourcePath resource path
1202 * @param id resource identifier
1203 * @return delete response
1204 */
1205 public static Response syncDelete(OpenstackHaService haService,
1206 String resourcePath,
1207 String id) {
1208
1209 log.debug("Sync DELETE request with {} on {}",
1210 haService.getActiveIp().toString(), resourcePath);
1211
1212 return getActiveClient(haService, resourcePath)
1213 .path("/" + id)
1214 .request(APPLICATION_JSON_TYPE)
1215 .delete();
1216 }
1217
Jian Li5ecfd1a2018-12-10 11:41:03 +09001218 private static Router getRouterFromSubnet(Subnet subnet,
1219 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001220 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1221 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1222 .findAny().orElse(null);
1223 if (osRouterIface == null) {
1224 return null;
1225 }
1226
1227 return osRouterService.router(osRouterIface.getId());
1228 }
1229
Daniel Park7e8c4d82018-08-13 23:47:49 +09001230 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001231 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001232 }
1233
Daniel Park2ff66b42018-08-01 11:52:45 +09001234 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001235 * Returns GARP packet with supplied floating ip and instance port information.
1236 *
1237 * @param floatingIP floating ip
1238 * @param instancePort instance port
1239 * @param vlanId vlan id
1240 * @return GARP packet
1241 */
1242 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1243 InstancePort instancePort,
1244 VlanId vlanId) {
1245 Ethernet ethernet = new Ethernet();
1246 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1247 ethernet.setSourceMACAddress(instancePort.macAddress());
1248 ethernet.setEtherType(Ethernet.TYPE_ARP);
1249 ethernet.setVlanID(vlanId.id());
1250
1251 ARP arp = new ARP();
1252 arp.setOpCode(ARP.OP_REPLY);
1253 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1254 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1255
1256 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1257 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1258
1259 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1260 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1261
Jian Li5ecfd1a2018-12-10 11:41:03 +09001262 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1263 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001264
1265 ethernet.setPayload(arp);
1266
1267 return ethernet;
1268 }
1269
1270 /**
Jian Li51b844c2018-05-31 10:59:03 +09001271 * Builds up and a complete endpoint URL from gateway node.
1272 *
1273 * @param node gateway node
1274 * @return a complete endpoint URL
1275 */
1276 private static String buildEndpoint(OpenstackNode node) {
1277
Jian Lic704b672018-09-04 18:52:53 +09001278 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001279
1280 StringBuilder endpointSb = new StringBuilder();
1281 endpointSb.append(auth.protocol().name().toLowerCase());
1282 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001283 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001284 return endpointSb.toString();
1285 }
1286
1287 /**
1288 * Obtains the SSL config without verifying the certification.
1289 *
1290 * @return SSL config
1291 */
1292 private static Config getSslConfig() {
1293 // we bypass the SSL certification verification for now
1294 // TODO: verify server side SSL using a given certification
1295 Config config = Config.newConfig().withSSLVerificationDisabled();
1296
1297 TrustManager[] trustAllCerts = new TrustManager[]{
1298 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001299 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001300 public X509Certificate[] getAcceptedIssuers() {
1301 return null;
1302 }
1303
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001304 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001305 public void checkClientTrusted(X509Certificate[] certs,
1306 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001307 return;
Jian Li51b844c2018-05-31 10:59:03 +09001308 }
1309
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001310 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001311 public void checkServerTrusted(X509Certificate[] certs,
1312 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001313 return;
Jian Li51b844c2018-05-31 10:59:03 +09001314 }
1315 }
1316 };
1317
1318 HostnameVerifier allHostsValid = (hostname, session) -> true;
1319
1320 try {
1321 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1322 sc.init(null, trustAllCerts,
1323 new java.security.SecureRandom());
1324 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1325 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1326
1327 config.withSSLContext(sc);
1328 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001329 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001330 return null;
1331 }
1332
1333 return config;
1334 }
1335
1336 /**
1337 * Obtains the facing object with given openstack perspective.
1338 *
1339 * @param perspective keystone perspective
1340 * @return facing object
1341 */
1342 private static Facing getFacing(Perspective perspective) {
1343
1344 switch (perspective) {
1345 case PUBLIC:
1346 return Facing.PUBLIC;
1347 case ADMIN:
1348 return Facing.ADMIN;
1349 case INTERNAL:
1350 return Facing.INTERNAL;
1351 default:
1352 return null;
1353 }
1354 }
1355
1356 /**
1357 * Obtains gateway instance by giving index number.
1358 *
1359 * @param gws a collection of gateway nodes
1360 * @param index index number
1361 * @return gateway instance
1362 */
1363 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1364 Map<String, OpenstackNode> hashMap = new HashMap<>();
1365 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1366 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1367 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1368
1369 int intIndex = 0;
1370 OpenstackNode gw = null;
1371 while (iteratorKey.hasNext()) {
1372 String key = iteratorKey.next();
1373
1374 if (intIndex == index) {
1375 gw = treeMap.get(key);
1376 }
1377 intIndex++;
1378 }
1379 return gw;
1380 }
Jian Li63430202018-08-30 16:24:09 +09001381}