blob: ce12d77424784278cd2449ba5d2847e3334d0852 [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;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090045import org.onlab.packet.ARP;
46import org.onlab.packet.Ethernet;
47import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090048import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090049import org.onlab.packet.MacAddress;
50import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090051import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090052import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090053import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090054import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090055import org.onosproject.net.flow.DefaultTrafficTreatment;
56import org.onosproject.net.flow.TrafficTreatment;
57import org.onosproject.net.packet.DefaultOutboundPacket;
58import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090059import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090060import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090061import org.onosproject.openstacknetworking.api.InstancePort;
SONA Project6bc5c4a2018-12-14 23:49:52 +090062import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090063import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090064import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090065import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090066import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090067import org.onosproject.openstacknode.api.OpenstackAuth;
68import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090069import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090070import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51b844c2018-05-31 10:59:03 +090071import org.openstack4j.api.OSClient;
72import org.openstack4j.api.client.IOSClientBuilder;
73import org.openstack4j.api.exceptions.AuthenticationException;
74import org.openstack4j.api.types.Facing;
75import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090076import org.openstack4j.core.transport.ObjectMapperSingleton;
77import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090078import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090079import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090080import org.openstack4j.model.network.NetFloatingIP;
81import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090082import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090083import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090084import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090085import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090086import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090087import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090088import org.slf4j.Logger;
89import org.slf4j.LoggerFactory;
90
Jian Li63430202018-08-30 16:24:09 +090091import javax.crypto.Mac;
92import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090093import javax.net.ssl.HostnameVerifier;
94import javax.net.ssl.HttpsURLConnection;
95import javax.net.ssl.SSLContext;
96import javax.net.ssl.TrustManager;
97import javax.net.ssl.X509TrustManager;
Jian Li63430202018-08-30 16:24:09 +090098import java.io.ByteArrayInputStream;
99import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900100import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900101import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900102import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900103import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900104import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900105import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900106import java.util.HashMap;
107import java.util.Iterator;
108import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900109import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900110import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900111import java.util.Set;
112import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900113import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900114
115import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900116import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900117import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900118import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900119import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900120import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Daniel Parkc4d06402018-05-28 15:57:37 +0900121import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
122import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900123import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
124import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Daniel Parkec9d1132018-08-19 11:18:03 +0900125import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700126import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900127import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900128
129/**
130 * An utility that used in openstack networking app.
131 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900132public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900133
Daniel Park95985382018-07-23 11:38:07 +0900134 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900135
Daniel Parkc4d06402018-05-28 15:57:37 +0900136 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900137 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900138 private static final String PREFIX_DEVICE_NUMBER = "s";
139 private static final String PREFIX_FUNCTION_NUMBER = "f";
140
Jian Li51b844c2018-05-31 10:59:03 +0900141 // keystone endpoint related variables
142 private static final String DOMAIN_DEFAULT = "default";
143 private static final String KEYSTONE_V2 = "v2.0";
144 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900145 private static final String SSL_TYPE = "SSL";
146
Jian Li7f024de2018-07-07 03:51:02 +0900147 private static final String PROXY_MODE = "proxy";
148 private static final String BROADCAST_MODE = "broadcast";
149
Jian Licad36c72018-09-13 17:44:54 +0900150 private static final String ENABLE = "enable";
151 private static final String DISABLE = "disable";
152
Jian Li63430202018-08-30 16:24:09 +0900153 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
154
155 private static final String HMAC_SHA256 = "HmacSHA256";
156
Jian Li24ec59f2018-05-23 19:01:25 +0900157 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
158
Daniel Parka73c2362018-09-17 17:43:25 +0900159 private static final String DL_DST = "dl_dst=";
160 private static final String NW_DST = "nw_dst=";
161 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
162 private static final String IN_PORT = "in_port=";
163 private static final String NW_SRC = "nw_src=";
164 private static final String COMMA = ",";
165 private static final String TUN_ID = "tun_id=";
166
167 private static final long TIMEOUT_MS = 5000;
168 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
169 private static final int SSH_PORT = 22;
170
Jian Li091d8d22018-02-20 10:42:06 +0900171 /**
172 * Prevents object instantiation from external.
173 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900174 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900175 }
176
177 /**
178 * Interprets JSON string to corresponding openstack model entity object.
179 *
180 * @param input JSON string
181 * @param entityClazz openstack model entity class
182 * @return openstack model entity object
183 */
184 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
185 ObjectMapper mapper = new ObjectMapper();
186 try {
187 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
188 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
189 return ObjectMapperSingleton.getContext(entityClazz)
190 .readerFor(entityClazz)
191 .readValue(jsonTree);
192 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900193 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900194 throw new IllegalArgumentException();
195 }
196 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900197
198 /**
199 * Converts openstack model entity object into JSON object.
200 *
201 * @param entity openstack model entity object
202 * @param entityClazz openstack model entity class
203 * @return JSON object
204 */
205 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
206 ObjectMapper mapper = new ObjectMapper();
207 try {
208 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
209 .writerFor(entityClazz)
210 .writeValueAsString(entity);
211 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900212 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900213 } catch (IOException e) {
214 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900215 throw new IllegalStateException();
216 }
217 }
Jian Li1064e4f2018-05-29 16:16:53 +0900218
219 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900220 * Obtains a floating IP associated with the given instance port.
221 *
222 * @param port instance port
223 * @param fips a collection of floating IPs
224 * @return associated floating IP
225 */
226 public static NetFloatingIP associatedFloatingIp(InstancePort port,
227 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900228 for (NetFloatingIP fip : fips) {
229 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
230 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900231 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900232 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
233 continue;
234 }
Jian Li6bc29d92018-10-02 13:55:05 +0900235 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
236 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900237 return fip;
238 }
Jian Li24ec59f2018-05-23 19:01:25 +0900239 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900240
Jian Li24ec59f2018-05-23 19:01:25 +0900241 return null;
242 }
243
244 /**
245 * Checks whether the given floating IP is associated with a VM.
246 *
247 * @param service openstack network service
248 * @param fip floating IP
249 * @return true if the given floating IP associated with a VM, false otherwise
250 */
251 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
252 NetFloatingIP fip) {
253 Port osPort = service.port(fip.getPortId());
254 if (osPort == null) {
255 return false;
256 }
257
258 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
259 Network osNet = service.network(osPort.getNetworkId());
260 if (osNet == null) {
261 final String errorFormat = ERR_FLOW + "no network(%s) exists";
262 final String error = String.format(errorFormat,
263 fip.getFloatingIpAddress(), osPort.getNetworkId());
264 throw new IllegalStateException(error);
265 }
266 return true;
267 } else {
268 return false;
269 }
270 }
271
272 /**
Jian Lia171a432018-06-11 11:52:11 +0900273 * Obtains the gateway node by instance port.
274 *
275 * @param gateways a collection of gateway nodes
276 * @param instPort instance port
277 * @return a gateway node
278 */
279 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
280 InstancePort instPort) {
281 OpenstackNode gw = null;
282 if (instPort != null && instPort.deviceId() != null) {
283 gw = getGwByComputeDevId(gateways, instPort.deviceId());
284 }
285 return gw;
286 }
287
288 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900289 * Obtains the gateway node by device in compute node. Note that the gateway
290 * node is determined by device's device identifier.
291 *
292 * @param gws a collection of gateway nodes
293 * @param deviceId device identifier
294 * @return a gateway node
295 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900296 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
297 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900298 int numOfGw = gws.size();
299
300 if (numOfGw == 0) {
301 return null;
302 }
303
304 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
305
306 return getGwByIndex(gws, gwIndex);
307 }
308
Jian Li51b844c2018-05-31 10:59:03 +0900309 /**
310 * Obtains a connected openstack client.
311 *
312 * @param osNode openstack node
313 * @return a connected openstack client
314 */
315 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900316 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900317 String endpoint = buildEndpoint(osNode);
318 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900319
Jian Li51b844c2018-05-31 10:59:03 +0900320 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900321
Jian Li51b844c2018-05-31 10:59:03 +0900322 try {
323 if (endpoint.contains(KEYSTONE_V2)) {
324 IOSClientBuilder.V2 builder = OSFactory.builderV2()
325 .endpoint(endpoint)
326 .tenantName(auth.project())
327 .credentials(auth.username(), auth.password())
328 .withConfig(config);
329
330 if (perspective != null) {
331 builder.perspective(getFacing(perspective));
332 }
333
334 return builder.authenticate();
335 } else if (endpoint.contains(KEYSTONE_V3)) {
336
337 Identifier project = Identifier.byName(auth.project());
338 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
339
340 IOSClientBuilder.V3 builder = OSFactory.builderV3()
341 .endpoint(endpoint)
342 .credentials(auth.username(), auth.password(), domain)
343 .scopeToProject(project, domain)
344 .withConfig(config);
345
346 if (perspective != null) {
347 builder.perspective(getFacing(perspective));
348 }
349
350 return builder.authenticate();
351 } else {
352 log.warn("Unrecognized keystone version type");
353 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900354 }
Jian Li51b844c2018-05-31 10:59:03 +0900355 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900356 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900357 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900358 }
Jian Li1064e4f2018-05-29 16:16:53 +0900359 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900360
361 /**
362 * Extract the interface name with the supplied port.
363 *
364 * @param port port
365 * @return interface name
366 */
367 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900368 String intfName;
369
Daniel Park95985382018-07-23 11:38:07 +0900370 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900371 log.error("Port profile is not found");
372 return null;
373 }
374
Daniel Park95985382018-07-23 11:38:07 +0900375 if (!port.getProfile().containsKey(PCISLOT) ||
376 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900377 log.error("Failed to retrieve the interface name because of no " +
378 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900379 return null;
380 }
Jian Li51b844c2018-05-31 10:59:03 +0900381
Daniel Parkff178ba2018-11-23 15:57:24 +0900382 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
383
384 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
385 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
386 return UNSUPPORTED_VENDOR;
387 }
388
389 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
390
Daniel Parkc4d06402018-05-28 15:57:37 +0900391 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
392 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
393
394 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
395 .split(":")[2]
396 .split("\\.")[0];
397 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
398
399 String functionNumHex = port.getProfile().get(PCISLOT).toString()
400 .split(":")[2]
401 .split("\\.")[1];
402 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
403
Daniel Parkc4d06402018-05-28 15:57:37 +0900404 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
405 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
406 } else {
407 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
408 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
409 }
410
411 return intfName;
412 }
Jian Li51b844c2018-05-31 10:59:03 +0900413
414 /**
Daniel Park95f73312018-07-31 15:48:34 +0900415 * Check if the given interface is added to the given device or not.
416 *
417 * @param deviceId device ID
418 * @param intfName interface name
419 * @param deviceService device service
420 * @return true if the given interface is added to the given device or false otherwise
421 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900422 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
423 String intfName,
424 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900425 checkNotNull(deviceId);
426 checkNotNull(intfName);
427
Jian Li5ecfd1a2018-12-10 11:41:03 +0900428 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
429 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900430 }
431
432 /**
Jian Li0b564282018-06-20 00:50:53 +0900433 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900434 *
435 * @param osPort port
436 * @param adminService openstack admin service
437 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900438 public static void addRouterIface(Port osPort,
439 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900440 osPort.getFixedIps().forEach(p -> {
441 JsonNode jsonTree = new ObjectMapper().createObjectNode()
442 .put("id", osPort.getDeviceId())
443 .put("tenant_id", osPort.getTenantId())
444 .put("subnet_id", p.getSubnetId())
445 .put("port_id", osPort.getId());
446 try {
447 RouterInterface rIface = getContext(NeutronRouterInterface.class)
448 .readerFor(NeutronRouterInterface.class)
449 .readValue(jsonTree);
450 if (adminService.routerInterface(rIface.getPortId()) != null) {
451 adminService.updateRouterInterface(rIface);
452 } else {
453 adminService.addRouterInterface(rIface);
454 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900455 } catch (IOException e) {
456 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900457 }
458 });
459 }
460
461 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900462 * Obtains the property value with specified property key name.
463 *
464 * @param properties a collection of properties
465 * @param name key name
466 * @return mapping value
467 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900468 public static String getPropertyValue(Set<ConfigProperty> properties,
469 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900470 Optional<ConfigProperty> property =
471 properties.stream().filter(p -> p.name().equals(name)).findFirst();
472 return property.map(ConfigProperty::value).orElse(null);
473 }
474
475 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900476 * Obtains the boolean property value with specified property key name.
477 *
478 * @param properties a collection of properties
479 * @param name key name
480 * @return mapping value
481 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900482 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
483 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900484 Optional<ConfigProperty> property =
485 properties.stream().filter(p -> p.name().equals(name)).findFirst();
486
487 return property.map(ConfigProperty::asBoolean).orElse(false);
488 }
489
490 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900491 * Prints out the JSON string in pretty format.
492 *
493 * @param mapper Object mapper
494 * @param jsonString JSON string
495 * @return pretty formatted JSON string
496 */
497 public static String prettyJson(ObjectMapper mapper, String jsonString) {
498 try {
499 Object jsonObject = mapper.readValue(jsonString, Object.class);
500 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900501 } catch (JsonParseException e) {
502 log.debug("JsonParseException caused by {}", e);
503 } catch (JsonMappingException e) {
504 log.debug("JsonMappingException caused by {}", e);
505 } catch (JsonProcessingException e) {
506 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900507 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900508 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900509 }
510 return null;
511 }
512
513 /**
Jian Li7f024de2018-07-07 03:51:02 +0900514 * Checks the validity of ARP mode.
515 *
516 * @param arpMode ARP mode
517 * @return returns true if the ARP mode is valid, false otherwise
518 */
519 public static boolean checkArpMode(String arpMode) {
520
521 if (isNullOrEmpty(arpMode)) {
522 return false;
523 } else {
524 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
525 }
526 }
527
528 /**
Jian Licad36c72018-09-13 17:44:54 +0900529 * Checks the validity of activation flag.
530 *
531 * @param activationFlag activation flag
532 * @return returns true if the activation flag is valid, false otherwise
533 */
534 public static boolean checkActivationFlag(String activationFlag) {
535
536 switch (activationFlag) {
537 case ENABLE:
538 return true;
539 case DISABLE:
540 return false;
541 default:
542 throw new IllegalArgumentException("The given activation flag is not valid!");
543 }
544 }
545
546 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900547 * Swaps current location with old location info.
548 * The revised instance port will be used to mod the flow rules after migration.
549 *
550 * @param instPort instance port
551 * @return location swapped instance port
552 */
553 public static InstancePort swapStaleLocation(InstancePort instPort) {
554 return DefaultInstancePort.builder()
555 .deviceId(instPort.oldDeviceId())
556 .portNumber(instPort.oldPortNumber())
557 .state(instPort.state())
558 .ipAddress(instPort.ipAddress())
559 .macAddress(instPort.macAddress())
560 .networkId(instPort.networkId())
561 .portId(instPort.portId())
562 .build();
563 }
564
565 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900566 * Compares two router interfaces are equal.
567 * Will be remove this after Openstack4j implements equals.
568 *
569 * @param routerInterface1 router interface
570 * @param routerInterface2 router interface
571 * @return returns true if two router interfaces are equal, false otherwise
572 */
Jian Li63430202018-08-30 16:24:09 +0900573 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
574 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900575 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
576 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
577 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
578 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
579 }
580
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900581 /**
582 * Returns the vnic type of given port.
583 *
584 * @param portName port name
585 * @return vnit type
586 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900587 public static VnicType vnicType(String portName) {
588 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
589 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
590 return VnicType.NORMAL;
591 } else if (isDirectPort(portName)) {
592 return VnicType.DIRECT;
593 } else {
594 return VnicType.UNSUPPORTED;
595 }
596 }
597
Jian Li63430202018-08-30 16:24:09 +0900598 /**
599 * Deserializes raw payload into HttpRequest object.
600 *
601 * @param rawData raw http payload
602 * @return HttpRequest object
603 */
604 public static HttpRequest parseHttpRequest(byte[] rawData) {
605 SessionInputBufferImpl sessionInputBuffer =
606 new SessionInputBufferImpl(
607 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
608 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900609 DefaultHttpRequestParser requestParser =
610 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900611 try {
612 return requestParser.parse();
613 } catch (IOException | HttpException e) {
614 log.warn("Failed to parse HttpRequest, due to {}", e);
615 }
616
617 return null;
618 }
619
620 /**
621 * Serializes HttpRequest object to byte array.
622 *
623 * @param request http request object
624 * @return byte array
625 */
626 public static byte[] unparseHttpRequest(HttpRequest request) {
627 try {
628 SessionOutputBufferImpl sessionOutputBuffer =
629 new SessionOutputBufferImpl(
630 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
631
632 ByteArrayOutputStream baos = new ByteArrayOutputStream();
633 sessionOutputBuffer.bind(baos);
634
Jian Li5ecfd1a2018-12-10 11:41:03 +0900635 HttpMessageWriter<HttpRequest> requestWriter =
636 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900637 requestWriter.write(request);
638 sessionOutputBuffer.flush();
639
640 return baos.toByteArray();
641 } catch (HttpException | IOException e) {
642 log.warn("Failed to unparse HttpRequest, due to {}", e);
643 }
644
645 return null;
646 }
647
648 /**
649 * Deserializes raw payload into HttpResponse object.
650 *
651 * @param rawData raw http payload
652 * @return HttpResponse object
653 */
654 public static HttpResponse parseHttpResponse(byte[] rawData) {
655 SessionInputBufferImpl sessionInputBuffer =
656 new SessionInputBufferImpl(
657 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
658 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900659 DefaultHttpResponseParser responseParser =
660 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900661 try {
662 return responseParser.parse();
663 } catch (IOException | HttpException e) {
664 log.warn("Failed to parse HttpResponse, due to {}", e);
665 }
666
667 return null;
668 }
669
670 /**
671 * Serializes HttpResponse header to byte array.
672 *
673 * @param response http response object
674 * @return byte array
675 */
676 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
677 try {
678 SessionOutputBufferImpl sessionOutputBuffer =
679 new SessionOutputBufferImpl(
680 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
681
682 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
683 sessionOutputBuffer.bind(headerBaos);
684
685 HttpMessageWriter<HttpResponse> responseWriter =
686 new DefaultHttpResponseWriter(sessionOutputBuffer);
687 responseWriter.write(response);
688 sessionOutputBuffer.flush();
689
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900690 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900691
692 return headerBaos.toByteArray();
693 } catch (IOException | HttpException e) {
694 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
695 }
696
697 return null;
698 }
699
700 /**
701 * Serializes HttpResponse object to byte array.
702 *
703 * @param response http response object
704 * @return byte array
705 */
706 public static byte[] unparseHttpResponseBody(HttpResponse response) {
707 try {
708 ByteArrayOutputStream baos = new ByteArrayOutputStream();
709 response.getEntity().writeTo(baos);
710
711 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900712 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900713
714 return baos.toByteArray();
715 } catch (IOException e) {
716 log.warn("Failed to unparse HttpResponse, due to {}", e);
717 }
718
719 return null;
720 }
721
722 /**
723 * Encodes the given data using HmacSHA256 encryption method with given secret key.
724 *
725 * @param key secret key
726 * @param data data to be encrypted
727 * @return Hmac256 encrypted data
728 */
729 public static String hmacEncrypt(String key, String data) {
730 try {
731 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
732 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
733 sha256Hmac.init(secretKey);
734 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
735 } catch (Exception e) {
736 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
737 }
738 return null;
739 }
740
Daniel Parka73c2362018-09-17 17:43:25 +0900741 /**
742 * Creates flow trace request string.
743 *
744 * @param srcIp src ip address
745 * @param dstIp dst ip address
746 * @param srcInstancePort src instance port
747 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900748 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900749 * @return flow trace request string
750 */
751 public static String traceRequestString(String srcIp,
752 String dstIp,
753 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900754 OpenstackNetworkService osNetService,
755 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900756
757 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
758
759 if (uplink) {
760
761 requestStringBuilder.append(COMMA)
762 .append(IN_PORT)
763 .append(srcInstancePort.portNumber().toString())
764 .append(COMMA)
765 .append(NW_SRC)
766 .append(srcIp)
767 .append(COMMA);
768
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900769 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900770 Type netType = osNetService.networkType(srcInstancePort.networkId());
771 if (netType == Type.VXLAN || netType == Type.GRE ||
772 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900773 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900774 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900775 requestStringBuilder.append(DL_DST)
776 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900777 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
778 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900779 requestStringBuilder.append(DL_DST)
780 .append(DEFAULT_GATEWAY_MAC_STR)
781 .append(COMMA);
782 }
783 } else {
784 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900785 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900786 }
787 }
788
789 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900790 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900791 .append("\n");
792 } else {
793 requestStringBuilder.append(COMMA)
794 .append(NW_SRC)
795 .append(dstIp)
796 .append(COMMA);
797
Jian Li621f73c2018-12-15 01:49:22 +0900798 Type netType = osNetService.networkType(srcInstancePort.networkId());
799
800 if (netType == Type.VXLAN || netType == Type.GRE ||
801 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900802 requestStringBuilder.append(TUN_ID)
803 .append(osNetService.segmentId(srcInstancePort.networkId()))
804 .append(COMMA);
805 }
806 requestStringBuilder.append(NW_DST)
807 .append(srcIp)
808 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900809 }
810
811 return requestStringBuilder.toString();
812 }
813
814 /**
815 * Sends flow trace string to node.
816 *
817 * @param requestString reqeust string
818 * @param node src node
819 * @return flow trace result in string format
820 */
821 public static String sendTraceRequestToNode(String requestString,
822 OpenstackNode node) {
823 String traceResult = null;
824 OpenstackSshAuth sshAuth = node.sshAuthInfo();
825
826 try (SshClient client = SshClient.setUpDefaultClient()) {
827 client.start();
828
829 try (ClientSession session = client
830 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
831 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
832 session.addPasswordIdentity(sshAuth.password());
833 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
834
835
836 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
837
838 log.debug("requestString: {}", requestString);
839 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900840 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900841
Jian Li5ecfd1a2018-12-10 11:41:03 +0900842 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900843 OutputStream errStream = new ByteArrayOutputStream();
844
845 channel.setIn(new NoCloseInputStream(inputStream));
846 channel.setErr(errStream);
847 channel.setOut(outputStream);
848
849 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
850 eventList.add(ClientChannelEvent.OPENED);
851
852 OpenFuture channelFuture = channel.open();
853
854 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
855
856 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
857
858 while (!channelFuture.isOpened()) {
859 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
860 log.error("Failed to open channel");
861 return null;
862 }
863 }
864 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
865
Jian Li5ecfd1a2018-12-10 11:41:03 +0900866 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900867
868 channel.close();
869 }
870 } finally {
871 session.close();
872 }
873 } finally {
874 client.stop();
875 }
876
877 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900878 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900879 }
880
881 return traceResult;
882 }
883
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900884 /**
885 * Returns the floating ip with supplied instance port.
886 *
887 * @param instancePort instance port
888 * @param osRouterAdminService openstack router admin service
889 * @return floating ip
890 */
891 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900892 OpenstackRouterAdminService
893 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900894 return osRouterAdminService.floatingIps().stream()
895 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
896 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
897 .findAny().orElse(null);
898 }
899
900 /**
901 * Sends GARP packet with supplied floating ip information.
902 *
903 * @param floatingIP floating ip
904 * @param instancePort instance port
905 * @param vlanId vlain id
906 * @param gatewayNode gateway node
907 * @param packetService packet service
908 */
Jian Li32b03622018-11-06 17:54:24 +0900909 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
910 InstancePort instancePort,
911 VlanId vlanId,
912 OpenstackNode gatewayNode,
913 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900914 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
915
916 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
917 .setOutput(gatewayNode.uplinkPortNum()).build();
918
919 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
920 ByteBuffer.wrap(ethernet.serialize())));
921 }
922
923 /**
924 * Returns the external peer router with supplied network information.
925 *
926 * @param network network
927 * @param osNetworkService openstack network service
928 * @param osRouterAdminService openstack router admin service
929 * @return external peer router
930 */
931 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900932 OpenstackNetworkService
933 osNetworkService,
934 OpenstackRouterAdminService
935 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900936 if (network == null) {
937 return null;
938 }
939
940 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
941
942 if (subnet == null) {
943 return null;
944 }
945
946 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
947 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
948 .findAny().orElse(null);
949 if (osRouterIface == null) {
950 return null;
951 }
952
953 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900954 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900955 return null;
956 }
957
958 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
959 return osNetworkService.externalPeerRouter(exGatewayInfo);
960
961 }
962
Jian Liebde74d2018-11-14 00:18:57 +0900963 /**
964 * Returns the external peer router with specified subnet information.
965 *
966 * @param subnet openstack subnet
967 * @param osRouterService openstack router service
968 * @param osNetworkService openstack network service
969 * @return external peer router
970 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900971 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
972 OpenstackRouterService
973 osRouterService,
974 OpenstackNetworkService
975 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900976 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
977 if (osRouter == null) {
978 return null;
979 }
980 if (osRouter.getExternalGatewayInfo() == null) {
981 // this router does not have external connectivity
982 log.trace("router({}) has no external gateway",
983 osRouter.getName());
984 return null;
985 }
986
987 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
988 }
989
990 /**
991 * Returns the external ip address with specified router information.
992 *
993 * @param srcSubnet source subnet
994 * @param osRouterService openstack router service
995 * @param osNetworkService openstack network service
996 * @return external ip address
997 */
998 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900999 OpenstackRouterService
1000 osRouterService,
1001 OpenstackNetworkService
1002 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001003
1004 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1005
1006 if (osRouter.getExternalGatewayInfo() == null) {
1007 // this router does not have external connectivity
1008 log.trace("router({}) has no external gateway",
1009 osRouter.getName());
1010 return null;
1011 }
1012
1013 return getExternalIp(osRouter, osNetworkService);
1014 }
1015
1016 /**
1017 * Returns the external ip address with specified router information.
1018 *
1019 * @param router openstack router
1020 * @param osNetworkService openstack network service
1021 * @return external ip address
1022 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001023 public static IpAddress getExternalIp(Router router,
1024 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001025 if (router == null) {
1026 return null;
1027 }
1028
1029 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1030 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001031 log.trace("Failed to get externalIp for router {} because " +
1032 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001033 router.getId());
1034 return null;
1035 }
1036
1037 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1038 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1039 .stream()
1040 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1041 .findAny().orElse(null);
1042
1043 if (exGatewayPort == null) {
1044 return null;
1045 }
1046
1047 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1048 .findAny().get().getIpAddress());
1049 }
1050
Jian Li2d68c192018-12-13 15:52:59 +09001051 /**
1052 * Returns the tunnel port number with specified net ID and openstack node.
1053 *
1054 * @param netId network ID
1055 * @param netService network service
1056 * @param osNode openstack node
1057 * @return tunnel port number
1058 */
1059 public static PortNumber tunnelPortNumByNetId(String netId,
1060 OpenstackNetworkService netService,
1061 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001062 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001063
1064 if (netType == null) {
1065 return null;
1066 }
1067
1068 return tunnelPortNumByNetType(netType, osNode);
1069 }
1070
1071 /**
1072 * Returns the tunnel port number with specified net type and openstack node.
1073 *
1074 * @param netType network type
1075 * @param osNode openstack node
1076 * @return tunnel port number
1077 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001078 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001079 switch (netType) {
1080 case VXLAN:
1081 return osNode.vxlanTunnelPortNum();
1082 case GRE:
1083 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001084 case GENEVE:
1085 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001086 default:
1087 return null;
1088 }
1089 }
1090
Jian Li5ecfd1a2018-12-10 11:41:03 +09001091 private static Router getRouterFromSubnet(Subnet subnet,
1092 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001093 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1094 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1095 .findAny().orElse(null);
1096 if (osRouterIface == null) {
1097 return null;
1098 }
1099
1100 return osRouterService.router(osRouterIface.getId());
1101 }
1102
Daniel Park7e8c4d82018-08-13 23:47:49 +09001103 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001104 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001105 }
1106
Daniel Park2ff66b42018-08-01 11:52:45 +09001107 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001108 * Returns GARP packet with supplied floating ip and instance port information.
1109 *
1110 * @param floatingIP floating ip
1111 * @param instancePort instance port
1112 * @param vlanId vlan id
1113 * @return GARP packet
1114 */
1115 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1116 InstancePort instancePort,
1117 VlanId vlanId) {
1118 Ethernet ethernet = new Ethernet();
1119 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1120 ethernet.setSourceMACAddress(instancePort.macAddress());
1121 ethernet.setEtherType(Ethernet.TYPE_ARP);
1122 ethernet.setVlanID(vlanId.id());
1123
1124 ARP arp = new ARP();
1125 arp.setOpCode(ARP.OP_REPLY);
1126 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1127 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1128
1129 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1130 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1131
1132 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1133 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1134
Jian Li5ecfd1a2018-12-10 11:41:03 +09001135 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1136 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001137
1138 ethernet.setPayload(arp);
1139
1140 return ethernet;
1141 }
1142
1143 /**
Jian Li51b844c2018-05-31 10:59:03 +09001144 * Builds up and a complete endpoint URL from gateway node.
1145 *
1146 * @param node gateway node
1147 * @return a complete endpoint URL
1148 */
1149 private static String buildEndpoint(OpenstackNode node) {
1150
Jian Lic704b672018-09-04 18:52:53 +09001151 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001152
1153 StringBuilder endpointSb = new StringBuilder();
1154 endpointSb.append(auth.protocol().name().toLowerCase());
1155 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001156 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001157 return endpointSb.toString();
1158 }
1159
1160 /**
1161 * Obtains the SSL config without verifying the certification.
1162 *
1163 * @return SSL config
1164 */
1165 private static Config getSslConfig() {
1166 // we bypass the SSL certification verification for now
1167 // TODO: verify server side SSL using a given certification
1168 Config config = Config.newConfig().withSSLVerificationDisabled();
1169
1170 TrustManager[] trustAllCerts = new TrustManager[]{
1171 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001172 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001173 public X509Certificate[] getAcceptedIssuers() {
1174 return null;
1175 }
1176
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001177 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001178 public void checkClientTrusted(X509Certificate[] certs,
1179 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001180 return;
Jian Li51b844c2018-05-31 10:59:03 +09001181 }
1182
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001183 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001184 public void checkServerTrusted(X509Certificate[] certs,
1185 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001186 return;
Jian Li51b844c2018-05-31 10:59:03 +09001187 }
1188 }
1189 };
1190
1191 HostnameVerifier allHostsValid = (hostname, session) -> true;
1192
1193 try {
1194 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1195 sc.init(null, trustAllCerts,
1196 new java.security.SecureRandom());
1197 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1198 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1199
1200 config.withSSLContext(sc);
1201 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001202 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001203 return null;
1204 }
1205
1206 return config;
1207 }
1208
1209 /**
1210 * Obtains the facing object with given openstack perspective.
1211 *
1212 * @param perspective keystone perspective
1213 * @return facing object
1214 */
1215 private static Facing getFacing(Perspective perspective) {
1216
1217 switch (perspective) {
1218 case PUBLIC:
1219 return Facing.PUBLIC;
1220 case ADMIN:
1221 return Facing.ADMIN;
1222 case INTERNAL:
1223 return Facing.INTERNAL;
1224 default:
1225 return null;
1226 }
1227 }
1228
1229 /**
1230 * Obtains gateway instance by giving index number.
1231 *
1232 * @param gws a collection of gateway nodes
1233 * @param index index number
1234 * @return gateway instance
1235 */
1236 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1237 Map<String, OpenstackNode> hashMap = new HashMap<>();
1238 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1239 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1240 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1241
1242 int intIndex = 0;
1243 OpenstackNode gw = null;
1244 while (iteratorKey.hasNext()) {
1245 String key = iteratorKey.next();
1246
1247 if (intIndex == index) {
1248 gw = treeMap.get(key);
1249 }
1250 intIndex++;
1251 }
1252 return gw;
1253 }
Jian Li63430202018-08-30 16:24:09 +09001254}