blob: 66d1f2a597443ffc2e651bc04eb95e2688dd10f5 [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;
Jian Li24ec59f2018-05-23 19:01:25 +090062import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090063import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090064import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090065import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090066import org.onosproject.openstacknode.api.OpenstackAuth;
67import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090068import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090069import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51b844c2018-05-31 10:59:03 +090070import org.openstack4j.api.OSClient;
71import org.openstack4j.api.client.IOSClientBuilder;
72import org.openstack4j.api.exceptions.AuthenticationException;
73import org.openstack4j.api.types.Facing;
74import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090075import org.openstack4j.core.transport.ObjectMapperSingleton;
76import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090077import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090078import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090079import org.openstack4j.model.network.NetFloatingIP;
80import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090081import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090082import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090083import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090084import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090085import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090086import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090087import org.slf4j.Logger;
88import org.slf4j.LoggerFactory;
89
Jian Li63430202018-08-30 16:24:09 +090090import javax.crypto.Mac;
91import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090092import javax.net.ssl.HostnameVerifier;
93import javax.net.ssl.HttpsURLConnection;
94import javax.net.ssl.SSLContext;
95import javax.net.ssl.TrustManager;
96import javax.net.ssl.X509TrustManager;
Jian Li63430202018-08-30 16:24:09 +090097import java.io.ByteArrayInputStream;
98import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +090099import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900100import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900101import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900102import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900103import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900104import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900105import java.util.HashMap;
106import java.util.Iterator;
107import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900108import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900109import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900110import java.util.Set;
111import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900112import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900113
114import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900115import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900116import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900117import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900118import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900119import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Daniel Parkc4d06402018-05-28 15:57:37 +0900120import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
121import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900122import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
123import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Daniel Parkec9d1132018-08-19 11:18:03 +0900124import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700125import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900126import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900127
128/**
129 * An utility that used in openstack networking app.
130 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900131public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900132
Daniel Park95985382018-07-23 11:38:07 +0900133 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900134
Daniel Parkc4d06402018-05-28 15:57:37 +0900135 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900136 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900137 private static final String PREFIX_DEVICE_NUMBER = "s";
138 private static final String PREFIX_FUNCTION_NUMBER = "f";
139
Jian Li51b844c2018-05-31 10:59:03 +0900140 // keystone endpoint related variables
141 private static final String DOMAIN_DEFAULT = "default";
142 private static final String KEYSTONE_V2 = "v2.0";
143 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900144 private static final String SSL_TYPE = "SSL";
145
Jian Li7f024de2018-07-07 03:51:02 +0900146 private static final String PROXY_MODE = "proxy";
147 private static final String BROADCAST_MODE = "broadcast";
148
Jian Licad36c72018-09-13 17:44:54 +0900149 private static final String ENABLE = "enable";
150 private static final String DISABLE = "disable";
151
Jian Li63430202018-08-30 16:24:09 +0900152 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
153
154 private static final String HMAC_SHA256 = "HmacSHA256";
155
Jian Li24ec59f2018-05-23 19:01:25 +0900156 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
157
Daniel Parka73c2362018-09-17 17:43:25 +0900158 private static final String VXLAN = "VXLAN";
Jian Li2d68c192018-12-13 15:52:59 +0900159 private static final String GRE = "GRE";
Daniel Parka73c2362018-09-17 17:43:25 +0900160 private static final String VLAN = "VLAN";
161 private static final String DL_DST = "dl_dst=";
162 private static final String NW_DST = "nw_dst=";
163 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
164 private static final String IN_PORT = "in_port=";
165 private static final String NW_SRC = "nw_src=";
166 private static final String COMMA = ",";
167 private static final String TUN_ID = "tun_id=";
168
169 private static final long TIMEOUT_MS = 5000;
170 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
171 private static final int SSH_PORT = 22;
172
Jian Li091d8d22018-02-20 10:42:06 +0900173 /**
174 * Prevents object instantiation from external.
175 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900176 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900177 }
178
179 /**
180 * Interprets JSON string to corresponding openstack model entity object.
181 *
182 * @param input JSON string
183 * @param entityClazz openstack model entity class
184 * @return openstack model entity object
185 */
186 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
187 ObjectMapper mapper = new ObjectMapper();
188 try {
189 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
190 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
191 return ObjectMapperSingleton.getContext(entityClazz)
192 .readerFor(entityClazz)
193 .readValue(jsonTree);
194 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900195 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900196 throw new IllegalArgumentException();
197 }
198 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900199
200 /**
201 * Converts openstack model entity object into JSON object.
202 *
203 * @param entity openstack model entity object
204 * @param entityClazz openstack model entity class
205 * @return JSON object
206 */
207 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
208 ObjectMapper mapper = new ObjectMapper();
209 try {
210 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
211 .writerFor(entityClazz)
212 .writeValueAsString(entity);
213 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900214 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900215 } catch (IOException e) {
216 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900217 throw new IllegalStateException();
218 }
219 }
Jian Li1064e4f2018-05-29 16:16:53 +0900220
221 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900222 * Obtains a floating IP associated with the given instance port.
223 *
224 * @param port instance port
225 * @param fips a collection of floating IPs
226 * @return associated floating IP
227 */
228 public static NetFloatingIP associatedFloatingIp(InstancePort port,
229 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900230 for (NetFloatingIP fip : fips) {
231 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
232 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900233 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900234 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
235 continue;
236 }
Jian Li6bc29d92018-10-02 13:55:05 +0900237 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
238 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900239 return fip;
240 }
Jian Li24ec59f2018-05-23 19:01:25 +0900241 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900242
Jian Li24ec59f2018-05-23 19:01:25 +0900243 return null;
244 }
245
246 /**
247 * Checks whether the given floating IP is associated with a VM.
248 *
249 * @param service openstack network service
250 * @param fip floating IP
251 * @return true if the given floating IP associated with a VM, false otherwise
252 */
253 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
254 NetFloatingIP fip) {
255 Port osPort = service.port(fip.getPortId());
256 if (osPort == null) {
257 return false;
258 }
259
260 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
261 Network osNet = service.network(osPort.getNetworkId());
262 if (osNet == null) {
263 final String errorFormat = ERR_FLOW + "no network(%s) exists";
264 final String error = String.format(errorFormat,
265 fip.getFloatingIpAddress(), osPort.getNetworkId());
266 throw new IllegalStateException(error);
267 }
268 return true;
269 } else {
270 return false;
271 }
272 }
273
274 /**
Jian Lia171a432018-06-11 11:52:11 +0900275 * Obtains the gateway node by instance port.
276 *
277 * @param gateways a collection of gateway nodes
278 * @param instPort instance port
279 * @return a gateway node
280 */
281 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
282 InstancePort instPort) {
283 OpenstackNode gw = null;
284 if (instPort != null && instPort.deviceId() != null) {
285 gw = getGwByComputeDevId(gateways, instPort.deviceId());
286 }
287 return gw;
288 }
289
290 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900291 * Obtains the gateway node by device in compute node. Note that the gateway
292 * node is determined by device's device identifier.
293 *
294 * @param gws a collection of gateway nodes
295 * @param deviceId device identifier
296 * @return a gateway node
297 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900298 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
299 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900300 int numOfGw = gws.size();
301
302 if (numOfGw == 0) {
303 return null;
304 }
305
306 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
307
308 return getGwByIndex(gws, gwIndex);
309 }
310
Jian Li51b844c2018-05-31 10:59:03 +0900311 /**
312 * Obtains a connected openstack client.
313 *
314 * @param osNode openstack node
315 * @return a connected openstack client
316 */
317 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900318 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900319 String endpoint = buildEndpoint(osNode);
320 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900321
Jian Li51b844c2018-05-31 10:59:03 +0900322 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900323
Jian Li51b844c2018-05-31 10:59:03 +0900324 try {
325 if (endpoint.contains(KEYSTONE_V2)) {
326 IOSClientBuilder.V2 builder = OSFactory.builderV2()
327 .endpoint(endpoint)
328 .tenantName(auth.project())
329 .credentials(auth.username(), auth.password())
330 .withConfig(config);
331
332 if (perspective != null) {
333 builder.perspective(getFacing(perspective));
334 }
335
336 return builder.authenticate();
337 } else if (endpoint.contains(KEYSTONE_V3)) {
338
339 Identifier project = Identifier.byName(auth.project());
340 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
341
342 IOSClientBuilder.V3 builder = OSFactory.builderV3()
343 .endpoint(endpoint)
344 .credentials(auth.username(), auth.password(), domain)
345 .scopeToProject(project, domain)
346 .withConfig(config);
347
348 if (perspective != null) {
349 builder.perspective(getFacing(perspective));
350 }
351
352 return builder.authenticate();
353 } else {
354 log.warn("Unrecognized keystone version type");
355 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900356 }
Jian Li51b844c2018-05-31 10:59:03 +0900357 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900358 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900359 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900360 }
Jian Li1064e4f2018-05-29 16:16:53 +0900361 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900362
363 /**
364 * Extract the interface name with the supplied port.
365 *
366 * @param port port
367 * @return interface name
368 */
369 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900370 String intfName;
371
Daniel Park95985382018-07-23 11:38:07 +0900372 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900373 log.error("Port profile is not found");
374 return null;
375 }
376
Daniel Park95985382018-07-23 11:38:07 +0900377 if (!port.getProfile().containsKey(PCISLOT) ||
378 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900379 log.error("Failed to retrieve the interface name because of no " +
380 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900381 return null;
382 }
Jian Li51b844c2018-05-31 10:59:03 +0900383
Daniel Parkff178ba2018-11-23 15:57:24 +0900384 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
385
386 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
387 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
388 return UNSUPPORTED_VENDOR;
389 }
390
391 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
392
Daniel Parkc4d06402018-05-28 15:57:37 +0900393 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
394 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
395
396 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
397 .split(":")[2]
398 .split("\\.")[0];
399 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
400
401 String functionNumHex = port.getProfile().get(PCISLOT).toString()
402 .split(":")[2]
403 .split("\\.")[1];
404 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
405
Daniel Parkc4d06402018-05-28 15:57:37 +0900406 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
407 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
408 } else {
409 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
410 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
411 }
412
413 return intfName;
414 }
Jian Li51b844c2018-05-31 10:59:03 +0900415
416 /**
Daniel Park95f73312018-07-31 15:48:34 +0900417 * Check if the given interface is added to the given device or not.
418 *
419 * @param deviceId device ID
420 * @param intfName interface name
421 * @param deviceService device service
422 * @return true if the given interface is added to the given device or false otherwise
423 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900424 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
425 String intfName,
426 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900427 checkNotNull(deviceId);
428 checkNotNull(intfName);
429
Jian Li5ecfd1a2018-12-10 11:41:03 +0900430 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
431 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900432 }
433
434 /**
Jian Li0b564282018-06-20 00:50:53 +0900435 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900436 *
437 * @param osPort port
438 * @param adminService openstack admin service
439 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900440 public static void addRouterIface(Port osPort,
441 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900442 osPort.getFixedIps().forEach(p -> {
443 JsonNode jsonTree = new ObjectMapper().createObjectNode()
444 .put("id", osPort.getDeviceId())
445 .put("tenant_id", osPort.getTenantId())
446 .put("subnet_id", p.getSubnetId())
447 .put("port_id", osPort.getId());
448 try {
449 RouterInterface rIface = getContext(NeutronRouterInterface.class)
450 .readerFor(NeutronRouterInterface.class)
451 .readValue(jsonTree);
452 if (adminService.routerInterface(rIface.getPortId()) != null) {
453 adminService.updateRouterInterface(rIface);
454 } else {
455 adminService.addRouterInterface(rIface);
456 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900457 } catch (IOException e) {
458 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900459 }
460 });
461 }
462
463 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900464 * Obtains the property value with specified property key name.
465 *
466 * @param properties a collection of properties
467 * @param name key name
468 * @return mapping value
469 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900470 public static String getPropertyValue(Set<ConfigProperty> properties,
471 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900472 Optional<ConfigProperty> property =
473 properties.stream().filter(p -> p.name().equals(name)).findFirst();
474 return property.map(ConfigProperty::value).orElse(null);
475 }
476
477 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900478 * Obtains the boolean property value with specified property key name.
479 *
480 * @param properties a collection of properties
481 * @param name key name
482 * @return mapping value
483 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900484 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
485 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900486 Optional<ConfigProperty> property =
487 properties.stream().filter(p -> p.name().equals(name)).findFirst();
488
489 return property.map(ConfigProperty::asBoolean).orElse(false);
490 }
491
492 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900493 * Prints out the JSON string in pretty format.
494 *
495 * @param mapper Object mapper
496 * @param jsonString JSON string
497 * @return pretty formatted JSON string
498 */
499 public static String prettyJson(ObjectMapper mapper, String jsonString) {
500 try {
501 Object jsonObject = mapper.readValue(jsonString, Object.class);
502 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900503 } catch (JsonParseException e) {
504 log.debug("JsonParseException caused by {}", e);
505 } catch (JsonMappingException e) {
506 log.debug("JsonMappingException caused by {}", e);
507 } catch (JsonProcessingException e) {
508 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900509 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900510 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900511 }
512 return null;
513 }
514
515 /**
Jian Li7f024de2018-07-07 03:51:02 +0900516 * Checks the validity of ARP mode.
517 *
518 * @param arpMode ARP mode
519 * @return returns true if the ARP mode is valid, false otherwise
520 */
521 public static boolean checkArpMode(String arpMode) {
522
523 if (isNullOrEmpty(arpMode)) {
524 return false;
525 } else {
526 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
527 }
528 }
529
530 /**
Jian Licad36c72018-09-13 17:44:54 +0900531 * Checks the validity of activation flag.
532 *
533 * @param activationFlag activation flag
534 * @return returns true if the activation flag is valid, false otherwise
535 */
536 public static boolean checkActivationFlag(String activationFlag) {
537
538 switch (activationFlag) {
539 case ENABLE:
540 return true;
541 case DISABLE:
542 return false;
543 default:
544 throw new IllegalArgumentException("The given activation flag is not valid!");
545 }
546 }
547
548 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900549 * Swaps current location with old location info.
550 * The revised instance port will be used to mod the flow rules after migration.
551 *
552 * @param instPort instance port
553 * @return location swapped instance port
554 */
555 public static InstancePort swapStaleLocation(InstancePort instPort) {
556 return DefaultInstancePort.builder()
557 .deviceId(instPort.oldDeviceId())
558 .portNumber(instPort.oldPortNumber())
559 .state(instPort.state())
560 .ipAddress(instPort.ipAddress())
561 .macAddress(instPort.macAddress())
562 .networkId(instPort.networkId())
563 .portId(instPort.portId())
564 .build();
565 }
566
567 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900568 * Compares two router interfaces are equal.
569 * Will be remove this after Openstack4j implements equals.
570 *
571 * @param routerInterface1 router interface
572 * @param routerInterface2 router interface
573 * @return returns true if two router interfaces are equal, false otherwise
574 */
Jian Li63430202018-08-30 16:24:09 +0900575 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
576 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900577 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
578 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
579 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
580 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
581 }
582
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900583 /**
584 * Returns the vnic type of given port.
585 *
586 * @param portName port name
587 * @return vnit type
588 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900589 public static VnicType vnicType(String portName) {
590 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
591 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
592 return VnicType.NORMAL;
593 } else if (isDirectPort(portName)) {
594 return VnicType.DIRECT;
595 } else {
596 return VnicType.UNSUPPORTED;
597 }
598 }
599
Jian Li63430202018-08-30 16:24:09 +0900600 /**
601 * Deserializes raw payload into HttpRequest object.
602 *
603 * @param rawData raw http payload
604 * @return HttpRequest object
605 */
606 public static HttpRequest parseHttpRequest(byte[] rawData) {
607 SessionInputBufferImpl sessionInputBuffer =
608 new SessionInputBufferImpl(
609 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
610 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900611 DefaultHttpRequestParser requestParser =
612 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900613 try {
614 return requestParser.parse();
615 } catch (IOException | HttpException e) {
616 log.warn("Failed to parse HttpRequest, due to {}", e);
617 }
618
619 return null;
620 }
621
622 /**
623 * Serializes HttpRequest object to byte array.
624 *
625 * @param request http request object
626 * @return byte array
627 */
628 public static byte[] unparseHttpRequest(HttpRequest request) {
629 try {
630 SessionOutputBufferImpl sessionOutputBuffer =
631 new SessionOutputBufferImpl(
632 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
633
634 ByteArrayOutputStream baos = new ByteArrayOutputStream();
635 sessionOutputBuffer.bind(baos);
636
Jian Li5ecfd1a2018-12-10 11:41:03 +0900637 HttpMessageWriter<HttpRequest> requestWriter =
638 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900639 requestWriter.write(request);
640 sessionOutputBuffer.flush();
641
642 return baos.toByteArray();
643 } catch (HttpException | IOException e) {
644 log.warn("Failed to unparse HttpRequest, due to {}", e);
645 }
646
647 return null;
648 }
649
650 /**
651 * Deserializes raw payload into HttpResponse object.
652 *
653 * @param rawData raw http payload
654 * @return HttpResponse object
655 */
656 public static HttpResponse parseHttpResponse(byte[] rawData) {
657 SessionInputBufferImpl sessionInputBuffer =
658 new SessionInputBufferImpl(
659 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
660 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900661 DefaultHttpResponseParser responseParser =
662 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900663 try {
664 return responseParser.parse();
665 } catch (IOException | HttpException e) {
666 log.warn("Failed to parse HttpResponse, due to {}", e);
667 }
668
669 return null;
670 }
671
672 /**
673 * Serializes HttpResponse header to byte array.
674 *
675 * @param response http response object
676 * @return byte array
677 */
678 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
679 try {
680 SessionOutputBufferImpl sessionOutputBuffer =
681 new SessionOutputBufferImpl(
682 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
683
684 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
685 sessionOutputBuffer.bind(headerBaos);
686
687 HttpMessageWriter<HttpResponse> responseWriter =
688 new DefaultHttpResponseWriter(sessionOutputBuffer);
689 responseWriter.write(response);
690 sessionOutputBuffer.flush();
691
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900692 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900693
694 return headerBaos.toByteArray();
695 } catch (IOException | HttpException e) {
696 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
697 }
698
699 return null;
700 }
701
702 /**
703 * Serializes HttpResponse object to byte array.
704 *
705 * @param response http response object
706 * @return byte array
707 */
708 public static byte[] unparseHttpResponseBody(HttpResponse response) {
709 try {
710 ByteArrayOutputStream baos = new ByteArrayOutputStream();
711 response.getEntity().writeTo(baos);
712
713 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900714 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900715
716 return baos.toByteArray();
717 } catch (IOException e) {
718 log.warn("Failed to unparse HttpResponse, due to {}", e);
719 }
720
721 return null;
722 }
723
724 /**
725 * Encodes the given data using HmacSHA256 encryption method with given secret key.
726 *
727 * @param key secret key
728 * @param data data to be encrypted
729 * @return Hmac256 encrypted data
730 */
731 public static String hmacEncrypt(String key, String data) {
732 try {
733 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
734 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
735 sha256Hmac.init(secretKey);
736 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
737 } catch (Exception e) {
738 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
739 }
740 return null;
741 }
742
Daniel Parka73c2362018-09-17 17:43:25 +0900743 /**
744 * Creates flow trace request string.
745 *
746 * @param srcIp src ip address
747 * @param dstIp dst ip address
748 * @param srcInstancePort src instance port
749 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900750 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900751 * @return flow trace request string
752 */
753 public static String traceRequestString(String srcIp,
754 String dstIp,
755 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900756 OpenstackNetworkService osNetService,
757 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900758
759 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
760
761 if (uplink) {
762
763 requestStringBuilder.append(COMMA)
764 .append(IN_PORT)
765 .append(srcInstancePort.portNumber().toString())
766 .append(COMMA)
767 .append(NW_SRC)
768 .append(srcIp)
769 .append(COMMA);
770
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900771 String modifiedDstIp = dstIp;
Daniel Parka73c2362018-09-17 17:43:25 +0900772 if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
Jian Li2d68c192018-12-13 15:52:59 +0900773 osNetService.networkType(srcInstancePort.networkId()).equals(GRE) ||
Daniel Parka73c2362018-09-17 17:43:25 +0900774 osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
775 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900776 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900777 requestStringBuilder.append(DL_DST)
778 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900779 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
780 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900781 requestStringBuilder.append(DL_DST)
782 .append(DEFAULT_GATEWAY_MAC_STR)
783 .append(COMMA);
784 }
785 } else {
786 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900787 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900788 }
789 }
790
791 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900792 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900793 .append("\n");
794 } else {
795 requestStringBuilder.append(COMMA)
796 .append(NW_SRC)
797 .append(dstIp)
798 .append(COMMA);
799
800 if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
Jian Li2d68c192018-12-13 15:52:59 +0900801 osNetService.networkType(srcInstancePort.networkId()).equals(GRE) ||
Daniel Parka73c2362018-09-17 17:43:25 +0900802 osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
803 requestStringBuilder.append(TUN_ID)
804 .append(osNetService.segmentId(srcInstancePort.networkId()))
805 .append(COMMA);
806 }
807 requestStringBuilder.append(NW_DST)
808 .append(srcIp)
809 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900810 }
811
812 return requestStringBuilder.toString();
813 }
814
815 /**
816 * Sends flow trace string to node.
817 *
818 * @param requestString reqeust string
819 * @param node src node
820 * @return flow trace result in string format
821 */
822 public static String sendTraceRequestToNode(String requestString,
823 OpenstackNode node) {
824 String traceResult = null;
825 OpenstackSshAuth sshAuth = node.sshAuthInfo();
826
827 try (SshClient client = SshClient.setUpDefaultClient()) {
828 client.start();
829
830 try (ClientSession session = client
831 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
832 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
833 session.addPasswordIdentity(sshAuth.password());
834 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
835
836
837 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
838
839 log.debug("requestString: {}", requestString);
840 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900841 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900842
Jian Li5ecfd1a2018-12-10 11:41:03 +0900843 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900844 OutputStream errStream = new ByteArrayOutputStream();
845
846 channel.setIn(new NoCloseInputStream(inputStream));
847 channel.setErr(errStream);
848 channel.setOut(outputStream);
849
850 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
851 eventList.add(ClientChannelEvent.OPENED);
852
853 OpenFuture channelFuture = channel.open();
854
855 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
856
857 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
858
859 while (!channelFuture.isOpened()) {
860 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
861 log.error("Failed to open channel");
862 return null;
863 }
864 }
865 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
866
Jian Li5ecfd1a2018-12-10 11:41:03 +0900867 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900868
869 channel.close();
870 }
871 } finally {
872 session.close();
873 }
874 } finally {
875 client.stop();
876 }
877
878 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900879 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900880 }
881
882 return traceResult;
883 }
884
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900885 /**
886 * Returns the floating ip with supplied instance port.
887 *
888 * @param instancePort instance port
889 * @param osRouterAdminService openstack router admin service
890 * @return floating ip
891 */
892 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900893 OpenstackRouterAdminService
894 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900895 return osRouterAdminService.floatingIps().stream()
896 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
897 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
898 .findAny().orElse(null);
899 }
900
901 /**
902 * Sends GARP packet with supplied floating ip information.
903 *
904 * @param floatingIP floating ip
905 * @param instancePort instance port
906 * @param vlanId vlain id
907 * @param gatewayNode gateway node
908 * @param packetService packet service
909 */
Jian Li32b03622018-11-06 17:54:24 +0900910 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
911 InstancePort instancePort,
912 VlanId vlanId,
913 OpenstackNode gatewayNode,
914 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900915 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
916
917 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
918 .setOutput(gatewayNode.uplinkPortNum()).build();
919
920 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
921 ByteBuffer.wrap(ethernet.serialize())));
922 }
923
924 /**
925 * Returns the external peer router with supplied network information.
926 *
927 * @param network network
928 * @param osNetworkService openstack network service
929 * @param osRouterAdminService openstack router admin service
930 * @return external peer router
931 */
932 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900933 OpenstackNetworkService
934 osNetworkService,
935 OpenstackRouterAdminService
936 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900937 if (network == null) {
938 return null;
939 }
940
941 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
942
943 if (subnet == null) {
944 return null;
945 }
946
947 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
948 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
949 .findAny().orElse(null);
950 if (osRouterIface == null) {
951 return null;
952 }
953
954 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900955 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900956 return null;
957 }
958
959 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
960 return osNetworkService.externalPeerRouter(exGatewayInfo);
961
962 }
963
Jian Liebde74d2018-11-14 00:18:57 +0900964 /**
965 * Returns the external peer router with specified subnet information.
966 *
967 * @param subnet openstack subnet
968 * @param osRouterService openstack router service
969 * @param osNetworkService openstack network service
970 * @return external peer router
971 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900972 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
973 OpenstackRouterService
974 osRouterService,
975 OpenstackNetworkService
976 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900977 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
978 if (osRouter == null) {
979 return null;
980 }
981 if (osRouter.getExternalGatewayInfo() == null) {
982 // this router does not have external connectivity
983 log.trace("router({}) has no external gateway",
984 osRouter.getName());
985 return null;
986 }
987
988 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
989 }
990
991 /**
992 * Returns the external ip address with specified router information.
993 *
994 * @param srcSubnet source subnet
995 * @param osRouterService openstack router service
996 * @param osNetworkService openstack network service
997 * @return external ip address
998 */
999 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001000 OpenstackRouterService
1001 osRouterService,
1002 OpenstackNetworkService
1003 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001004
1005 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1006
1007 if (osRouter.getExternalGatewayInfo() == null) {
1008 // this router does not have external connectivity
1009 log.trace("router({}) has no external gateway",
1010 osRouter.getName());
1011 return null;
1012 }
1013
1014 return getExternalIp(osRouter, osNetworkService);
1015 }
1016
1017 /**
1018 * Returns the external ip address with specified router information.
1019 *
1020 * @param router openstack router
1021 * @param osNetworkService openstack network service
1022 * @return external ip address
1023 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001024 public static IpAddress getExternalIp(Router router,
1025 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001026 if (router == null) {
1027 return null;
1028 }
1029
1030 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1031 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001032 log.trace("Failed to get externalIp for router {} because " +
1033 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001034 router.getId());
1035 return null;
1036 }
1037
1038 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1039 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1040 .stream()
1041 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1042 .findAny().orElse(null);
1043
1044 if (exGatewayPort == null) {
1045 return null;
1046 }
1047
1048 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1049 .findAny().get().getIpAddress());
1050 }
1051
Jian Li2d68c192018-12-13 15:52:59 +09001052 /**
1053 * Returns the tunnel port number with specified net ID and openstack node.
1054 *
1055 * @param netId network ID
1056 * @param netService network service
1057 * @param osNode openstack node
1058 * @return tunnel port number
1059 */
1060 public static PortNumber tunnelPortNumByNetId(String netId,
1061 OpenstackNetworkService netService,
1062 OpenstackNode osNode) {
1063 String netType = netService.networkType(netId);
1064
1065 if (netType == null) {
1066 return null;
1067 }
1068
1069 return tunnelPortNumByNetType(netType, osNode);
1070 }
1071
1072 /**
1073 * Returns the tunnel port number with specified net type and openstack node.
1074 *
1075 * @param netType network type
1076 * @param osNode openstack node
1077 * @return tunnel port number
1078 */
1079 public static PortNumber tunnelPortNumByNetType(String netType, OpenstackNode osNode) {
1080 switch (netType) {
1081 case VXLAN:
1082 return osNode.vxlanTunnelPortNum();
1083 case GRE:
1084 return osNode.greTunnelPortNum();
1085 default:
1086 return null;
1087 }
1088 }
1089
Jian Li5ecfd1a2018-12-10 11:41:03 +09001090 private static Router getRouterFromSubnet(Subnet subnet,
1091 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001092 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1093 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1094 .findAny().orElse(null);
1095 if (osRouterIface == null) {
1096 return null;
1097 }
1098
1099 return osRouterService.router(osRouterIface.getId());
1100 }
1101
Daniel Park7e8c4d82018-08-13 23:47:49 +09001102 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001103 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001104 }
1105
Daniel Park2ff66b42018-08-01 11:52:45 +09001106 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001107 * Returns GARP packet with supplied floating ip and instance port information.
1108 *
1109 * @param floatingIP floating ip
1110 * @param instancePort instance port
1111 * @param vlanId vlan id
1112 * @return GARP packet
1113 */
1114 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1115 InstancePort instancePort,
1116 VlanId vlanId) {
1117 Ethernet ethernet = new Ethernet();
1118 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1119 ethernet.setSourceMACAddress(instancePort.macAddress());
1120 ethernet.setEtherType(Ethernet.TYPE_ARP);
1121 ethernet.setVlanID(vlanId.id());
1122
1123 ARP arp = new ARP();
1124 arp.setOpCode(ARP.OP_REPLY);
1125 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1126 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1127
1128 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1129 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1130
1131 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1132 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1133
Jian Li5ecfd1a2018-12-10 11:41:03 +09001134 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1135 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001136
1137 ethernet.setPayload(arp);
1138
1139 return ethernet;
1140 }
1141
1142 /**
Jian Li51b844c2018-05-31 10:59:03 +09001143 * Builds up and a complete endpoint URL from gateway node.
1144 *
1145 * @param node gateway node
1146 * @return a complete endpoint URL
1147 */
1148 private static String buildEndpoint(OpenstackNode node) {
1149
Jian Lic704b672018-09-04 18:52:53 +09001150 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001151
1152 StringBuilder endpointSb = new StringBuilder();
1153 endpointSb.append(auth.protocol().name().toLowerCase());
1154 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001155 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001156 return endpointSb.toString();
1157 }
1158
1159 /**
1160 * Obtains the SSL config without verifying the certification.
1161 *
1162 * @return SSL config
1163 */
1164 private static Config getSslConfig() {
1165 // we bypass the SSL certification verification for now
1166 // TODO: verify server side SSL using a given certification
1167 Config config = Config.newConfig().withSSLVerificationDisabled();
1168
1169 TrustManager[] trustAllCerts = new TrustManager[]{
1170 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001171 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001172 public X509Certificate[] getAcceptedIssuers() {
1173 return null;
1174 }
1175
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001176 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001177 public void checkClientTrusted(X509Certificate[] certs,
1178 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001179 return;
Jian Li51b844c2018-05-31 10:59:03 +09001180 }
1181
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001182 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001183 public void checkServerTrusted(X509Certificate[] certs,
1184 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001185 return;
Jian Li51b844c2018-05-31 10:59:03 +09001186 }
1187 }
1188 };
1189
1190 HostnameVerifier allHostsValid = (hostname, session) -> true;
1191
1192 try {
1193 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1194 sc.init(null, trustAllCerts,
1195 new java.security.SecureRandom());
1196 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1197 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1198
1199 config.withSSLContext(sc);
1200 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001201 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001202 return null;
1203 }
1204
1205 return config;
1206 }
1207
1208 /**
1209 * Obtains the facing object with given openstack perspective.
1210 *
1211 * @param perspective keystone perspective
1212 * @return facing object
1213 */
1214 private static Facing getFacing(Perspective perspective) {
1215
1216 switch (perspective) {
1217 case PUBLIC:
1218 return Facing.PUBLIC;
1219 case ADMIN:
1220 return Facing.ADMIN;
1221 case INTERNAL:
1222 return Facing.INTERNAL;
1223 default:
1224 return null;
1225 }
1226 }
1227
1228 /**
1229 * Obtains gateway instance by giving index number.
1230 *
1231 * @param gws a collection of gateway nodes
1232 * @param index index number
1233 * @return gateway instance
1234 */
1235 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1236 Map<String, OpenstackNode> hashMap = new HashMap<>();
1237 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1238 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1239 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1240
1241 int intIndex = 0;
1242 OpenstackNode gw = null;
1243 while (iteratorKey.hasNext()) {
1244 String key = iteratorKey.next();
1245
1246 if (intIndex == index) {
1247 gw = treeMap.get(key);
1248 }
1249 intIndex++;
1250 }
1251 return gw;
1252 }
Jian Li63430202018-08-30 16:24:09 +09001253}