blob: a6e9915cceef5937418002b41d27800505d2981e [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;
Daniel Park95f73312018-07-31 15:48:34 +090053import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090054import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.packet.DefaultOutboundPacket;
57import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090058import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090059import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090060import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090061import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090062import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090063import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090064import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090065import org.onosproject.openstacknode.api.OpenstackAuth;
66import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090067import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090068import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51b844c2018-05-31 10:59:03 +090069import org.openstack4j.api.OSClient;
70import org.openstack4j.api.client.IOSClientBuilder;
71import org.openstack4j.api.exceptions.AuthenticationException;
72import org.openstack4j.api.types.Facing;
73import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090074import org.openstack4j.core.transport.ObjectMapperSingleton;
75import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090076import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090077import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090078import org.openstack4j.model.network.NetFloatingIP;
79import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090080import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090081import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090082import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090083import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090084import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090085import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090086import org.slf4j.Logger;
87import org.slf4j.LoggerFactory;
88
Jian Li63430202018-08-30 16:24:09 +090089import javax.crypto.Mac;
90import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090091import javax.net.ssl.HostnameVerifier;
92import javax.net.ssl.HttpsURLConnection;
93import javax.net.ssl.SSLContext;
94import javax.net.ssl.TrustManager;
95import javax.net.ssl.X509TrustManager;
Jian Li63430202018-08-30 16:24:09 +090096import java.io.ByteArrayInputStream;
97import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +090098import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +090099import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900100import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900101import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900102import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900103import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900104import java.util.HashMap;
105import java.util.Iterator;
106import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900107import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900108import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900109import java.util.Set;
110import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900111import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900112
113import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900114import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900115import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900116import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900117import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900118import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Daniel Parkc4d06402018-05-28 15:57:37 +0900119import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
120import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900121import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
122import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Daniel Parkec9d1132018-08-19 11:18:03 +0900123import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700124import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900125import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900126
127/**
128 * An utility that used in openstack networking app.
129 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900130public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900131
Daniel Park95985382018-07-23 11:38:07 +0900132 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900133
Daniel Parkc4d06402018-05-28 15:57:37 +0900134 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900135 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900136 private static final String PREFIX_DEVICE_NUMBER = "s";
137 private static final String PREFIX_FUNCTION_NUMBER = "f";
138
Jian Li51b844c2018-05-31 10:59:03 +0900139 // keystone endpoint related variables
140 private static final String DOMAIN_DEFAULT = "default";
141 private static final String KEYSTONE_V2 = "v2.0";
142 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900143 private static final String SSL_TYPE = "SSL";
144
Jian Li7f024de2018-07-07 03:51:02 +0900145 private static final String PROXY_MODE = "proxy";
146 private static final String BROADCAST_MODE = "broadcast";
147
Jian Licad36c72018-09-13 17:44:54 +0900148 private static final String ENABLE = "enable";
149 private static final String DISABLE = "disable";
150
Jian Li63430202018-08-30 16:24:09 +0900151 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
152
153 private static final String HMAC_SHA256 = "HmacSHA256";
154
Jian Li24ec59f2018-05-23 19:01:25 +0900155 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
156
Daniel Parka73c2362018-09-17 17:43:25 +0900157 private static final String VXLAN = "VXLAN";
158 private static final String VLAN = "VLAN";
159 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;
Daniel Parka73c2362018-09-17 17:43:25 +0900770 if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
771 osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
772 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900773 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900774 requestStringBuilder.append(DL_DST)
775 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900776 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
777 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900778 requestStringBuilder.append(DL_DST)
779 .append(DEFAULT_GATEWAY_MAC_STR)
780 .append(COMMA);
781 }
782 } else {
783 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900784 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900785 }
786 }
787
788 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900789 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900790 .append("\n");
791 } else {
792 requestStringBuilder.append(COMMA)
793 .append(NW_SRC)
794 .append(dstIp)
795 .append(COMMA);
796
797 if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
798 osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
799 requestStringBuilder.append(TUN_ID)
800 .append(osNetService.segmentId(srcInstancePort.networkId()))
801 .append(COMMA);
802 }
803 requestStringBuilder.append(NW_DST)
804 .append(srcIp)
805 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900806 }
807
808 return requestStringBuilder.toString();
809 }
810
811 /**
812 * Sends flow trace string to node.
813 *
814 * @param requestString reqeust string
815 * @param node src node
816 * @return flow trace result in string format
817 */
818 public static String sendTraceRequestToNode(String requestString,
819 OpenstackNode node) {
820 String traceResult = null;
821 OpenstackSshAuth sshAuth = node.sshAuthInfo();
822
823 try (SshClient client = SshClient.setUpDefaultClient()) {
824 client.start();
825
826 try (ClientSession session = client
827 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
828 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
829 session.addPasswordIdentity(sshAuth.password());
830 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
831
832
833 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
834
835 log.debug("requestString: {}", requestString);
836 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900837 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900838
Jian Li5ecfd1a2018-12-10 11:41:03 +0900839 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900840 OutputStream errStream = new ByteArrayOutputStream();
841
842 channel.setIn(new NoCloseInputStream(inputStream));
843 channel.setErr(errStream);
844 channel.setOut(outputStream);
845
846 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
847 eventList.add(ClientChannelEvent.OPENED);
848
849 OpenFuture channelFuture = channel.open();
850
851 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
852
853 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
854
855 while (!channelFuture.isOpened()) {
856 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
857 log.error("Failed to open channel");
858 return null;
859 }
860 }
861 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
862
Jian Li5ecfd1a2018-12-10 11:41:03 +0900863 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900864
865 channel.close();
866 }
867 } finally {
868 session.close();
869 }
870 } finally {
871 client.stop();
872 }
873
874 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900875 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900876 }
877
878 return traceResult;
879 }
880
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900881 /**
882 * Returns the floating ip with supplied instance port.
883 *
884 * @param instancePort instance port
885 * @param osRouterAdminService openstack router admin service
886 * @return floating ip
887 */
888 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900889 OpenstackRouterAdminService
890 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900891 return osRouterAdminService.floatingIps().stream()
892 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
893 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
894 .findAny().orElse(null);
895 }
896
897 /**
898 * Sends GARP packet with supplied floating ip information.
899 *
900 * @param floatingIP floating ip
901 * @param instancePort instance port
902 * @param vlanId vlain id
903 * @param gatewayNode gateway node
904 * @param packetService packet service
905 */
Jian Li32b03622018-11-06 17:54:24 +0900906 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
907 InstancePort instancePort,
908 VlanId vlanId,
909 OpenstackNode gatewayNode,
910 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900911 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
912
913 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
914 .setOutput(gatewayNode.uplinkPortNum()).build();
915
916 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
917 ByteBuffer.wrap(ethernet.serialize())));
918 }
919
920 /**
921 * Returns the external peer router with supplied network information.
922 *
923 * @param network network
924 * @param osNetworkService openstack network service
925 * @param osRouterAdminService openstack router admin service
926 * @return external peer router
927 */
928 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900929 OpenstackNetworkService
930 osNetworkService,
931 OpenstackRouterAdminService
932 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900933 if (network == null) {
934 return null;
935 }
936
937 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
938
939 if (subnet == null) {
940 return null;
941 }
942
943 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
944 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
945 .findAny().orElse(null);
946 if (osRouterIface == null) {
947 return null;
948 }
949
950 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900951 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900952 return null;
953 }
954
955 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
956 return osNetworkService.externalPeerRouter(exGatewayInfo);
957
958 }
959
Jian Liebde74d2018-11-14 00:18:57 +0900960 /**
961 * Returns the external peer router with specified subnet information.
962 *
963 * @param subnet openstack subnet
964 * @param osRouterService openstack router service
965 * @param osNetworkService openstack network service
966 * @return external peer router
967 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900968 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
969 OpenstackRouterService
970 osRouterService,
971 OpenstackNetworkService
972 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900973 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
974 if (osRouter == null) {
975 return null;
976 }
977 if (osRouter.getExternalGatewayInfo() == null) {
978 // this router does not have external connectivity
979 log.trace("router({}) has no external gateway",
980 osRouter.getName());
981 return null;
982 }
983
984 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
985 }
986
987 /**
988 * Returns the external ip address with specified router information.
989 *
990 * @param srcSubnet source subnet
991 * @param osRouterService openstack router service
992 * @param osNetworkService openstack network service
993 * @return external ip address
994 */
995 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900996 OpenstackRouterService
997 osRouterService,
998 OpenstackNetworkService
999 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001000
1001 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1002
1003 if (osRouter.getExternalGatewayInfo() == null) {
1004 // this router does not have external connectivity
1005 log.trace("router({}) has no external gateway",
1006 osRouter.getName());
1007 return null;
1008 }
1009
1010 return getExternalIp(osRouter, osNetworkService);
1011 }
1012
1013 /**
1014 * Returns the external ip address with specified router information.
1015 *
1016 * @param router openstack router
1017 * @param osNetworkService openstack network service
1018 * @return external ip address
1019 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001020 public static IpAddress getExternalIp(Router router,
1021 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001022 if (router == null) {
1023 return null;
1024 }
1025
1026 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1027 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001028 log.trace("Failed to get externalIp for router {} because " +
1029 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001030 router.getId());
1031 return null;
1032 }
1033
1034 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1035 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1036 .stream()
1037 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1038 .findAny().orElse(null);
1039
1040 if (exGatewayPort == null) {
1041 return null;
1042 }
1043
1044 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1045 .findAny().get().getIpAddress());
1046 }
1047
Jian Li5ecfd1a2018-12-10 11:41:03 +09001048 private static Router getRouterFromSubnet(Subnet subnet,
1049 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001050 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1051 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1052 .findAny().orElse(null);
1053 if (osRouterIface == null) {
1054 return null;
1055 }
1056
1057 return osRouterService.router(osRouterIface.getId());
1058 }
1059
Daniel Park7e8c4d82018-08-13 23:47:49 +09001060 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001061 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001062 }
1063
Daniel Park2ff66b42018-08-01 11:52:45 +09001064 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001065 * Returns GARP packet with supplied floating ip and instance port information.
1066 *
1067 * @param floatingIP floating ip
1068 * @param instancePort instance port
1069 * @param vlanId vlan id
1070 * @return GARP packet
1071 */
1072 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1073 InstancePort instancePort,
1074 VlanId vlanId) {
1075 Ethernet ethernet = new Ethernet();
1076 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1077 ethernet.setSourceMACAddress(instancePort.macAddress());
1078 ethernet.setEtherType(Ethernet.TYPE_ARP);
1079 ethernet.setVlanID(vlanId.id());
1080
1081 ARP arp = new ARP();
1082 arp.setOpCode(ARP.OP_REPLY);
1083 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1084 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1085
1086 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1087 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1088
1089 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1090 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1091
Jian Li5ecfd1a2018-12-10 11:41:03 +09001092 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1093 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001094
1095 ethernet.setPayload(arp);
1096
1097 return ethernet;
1098 }
1099
1100 /**
Jian Li51b844c2018-05-31 10:59:03 +09001101 * Builds up and a complete endpoint URL from gateway node.
1102 *
1103 * @param node gateway node
1104 * @return a complete endpoint URL
1105 */
1106 private static String buildEndpoint(OpenstackNode node) {
1107
Jian Lic704b672018-09-04 18:52:53 +09001108 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001109
1110 StringBuilder endpointSb = new StringBuilder();
1111 endpointSb.append(auth.protocol().name().toLowerCase());
1112 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001113 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001114 return endpointSb.toString();
1115 }
1116
1117 /**
1118 * Obtains the SSL config without verifying the certification.
1119 *
1120 * @return SSL config
1121 */
1122 private static Config getSslConfig() {
1123 // we bypass the SSL certification verification for now
1124 // TODO: verify server side SSL using a given certification
1125 Config config = Config.newConfig().withSSLVerificationDisabled();
1126
1127 TrustManager[] trustAllCerts = new TrustManager[]{
1128 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001129 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001130 public X509Certificate[] getAcceptedIssuers() {
1131 return null;
1132 }
1133
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001134 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001135 public void checkClientTrusted(X509Certificate[] certs,
1136 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001137 return;
Jian Li51b844c2018-05-31 10:59:03 +09001138 }
1139
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001140 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001141 public void checkServerTrusted(X509Certificate[] certs,
1142 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001143 return;
Jian Li51b844c2018-05-31 10:59:03 +09001144 }
1145 }
1146 };
1147
1148 HostnameVerifier allHostsValid = (hostname, session) -> true;
1149
1150 try {
1151 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1152 sc.init(null, trustAllCerts,
1153 new java.security.SecureRandom());
1154 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1155 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1156
1157 config.withSSLContext(sc);
1158 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001159 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001160 return null;
1161 }
1162
1163 return config;
1164 }
1165
1166 /**
1167 * Obtains the facing object with given openstack perspective.
1168 *
1169 * @param perspective keystone perspective
1170 * @return facing object
1171 */
1172 private static Facing getFacing(Perspective perspective) {
1173
1174 switch (perspective) {
1175 case PUBLIC:
1176 return Facing.PUBLIC;
1177 case ADMIN:
1178 return Facing.ADMIN;
1179 case INTERNAL:
1180 return Facing.INTERNAL;
1181 default:
1182 return null;
1183 }
1184 }
1185
1186 /**
1187 * Obtains gateway instance by giving index number.
1188 *
1189 * @param gws a collection of gateway nodes
1190 * @param index index number
1191 * @return gateway instance
1192 */
1193 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1194 Map<String, OpenstackNode> hashMap = new HashMap<>();
1195 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1196 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1197 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1198
1199 int intIndex = 0;
1200 OpenstackNode gw = null;
1201 while (iteratorKey.hasNext()) {
1202 String key = iteratorKey.next();
1203
1204 if (intIndex == index) {
1205 gw = treeMap.get(key);
1206 }
1207 intIndex++;
1208 }
1209 return gw;
1210 }
Jian Li63430202018-08-30 16:24:09 +09001211}