blob: c4e380314c4458f6901b23a85846cb80fd791c20 [file] [log] [blame]
Jian Li091d8d22018-02-20 10:42:06 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknetworking.util;
17
Daniel Park2ff66b42018-08-01 11:52:45 +090018import com.fasterxml.jackson.core.JsonParseException;
19import com.fasterxml.jackson.core.JsonProcessingException;
20import com.fasterxml.jackson.databind.JsonMappingException;
Jian Li091d8d22018-02-20 10:42:06 +090021import com.fasterxml.jackson.databind.JsonNode;
22import com.fasterxml.jackson.databind.ObjectMapper;
Jian Lieb9f77d2018-02-20 11:25:45 +090023import com.fasterxml.jackson.databind.node.ObjectNode;
Daniel Parka73c2362018-09-17 17:43:25 +090024import com.google.common.base.Charsets;
Jian Li24ec59f2018-05-23 19:01:25 +090025import com.google.common.base.Strings;
Daniel Parka73c2362018-09-17 17:43:25 +090026import com.google.common.collect.Lists;
Jian Li63430202018-08-30 16:24:09 +090027import org.apache.commons.codec.binary.Hex;
28import org.apache.http.HttpException;
29import org.apache.http.HttpRequest;
30import org.apache.http.HttpResponse;
31import org.apache.http.impl.io.DefaultHttpRequestParser;
32import org.apache.http.impl.io.DefaultHttpRequestWriter;
33import org.apache.http.impl.io.DefaultHttpResponseParser;
34import org.apache.http.impl.io.DefaultHttpResponseWriter;
35import org.apache.http.impl.io.HttpTransportMetricsImpl;
36import org.apache.http.impl.io.SessionInputBufferImpl;
37import org.apache.http.impl.io.SessionOutputBufferImpl;
38import org.apache.http.io.HttpMessageWriter;
Daniel Parka73c2362018-09-17 17:43:25 +090039import org.apache.sshd.client.SshClient;
40import org.apache.sshd.client.channel.ClientChannel;
41import org.apache.sshd.client.channel.ClientChannelEvent;
42import org.apache.sshd.client.future.OpenFuture;
43import org.apache.sshd.client.session.ClientSession;
44import org.apache.sshd.common.util.io.NoCloseInputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090045import org.onlab.packet.ARP;
46import org.onlab.packet.Ethernet;
47import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090048import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090049import org.onlab.packet.MacAddress;
50import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090051import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090052import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090053import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090054import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090055import org.onosproject.net.flow.DefaultTrafficTreatment;
56import org.onosproject.net.flow.TrafficTreatment;
57import org.onosproject.net.packet.DefaultOutboundPacket;
58import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090059import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090060import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090061import org.onosproject.openstacknetworking.api.InstancePort;
SONA Project6bc5c4a2018-12-14 23:49:52 +090062import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090063import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090064import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090065import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090066import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090067import org.onosproject.openstacknode.api.OpenstackAuth;
68import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090069import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090070import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51b844c2018-05-31 10:59:03 +090071import org.openstack4j.api.OSClient;
72import org.openstack4j.api.client.IOSClientBuilder;
73import org.openstack4j.api.exceptions.AuthenticationException;
74import org.openstack4j.api.types.Facing;
75import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090076import org.openstack4j.core.transport.ObjectMapperSingleton;
77import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090078import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090079import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090080import org.openstack4j.model.network.NetFloatingIP;
81import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090082import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090083import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090084import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090085import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090086import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090087import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090088import org.slf4j.Logger;
89import org.slf4j.LoggerFactory;
90
Jian Li63430202018-08-30 16:24:09 +090091import javax.crypto.Mac;
92import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +090093import javax.net.ssl.HostnameVerifier;
94import javax.net.ssl.HttpsURLConnection;
95import javax.net.ssl.SSLContext;
96import javax.net.ssl.TrustManager;
97import javax.net.ssl.X509TrustManager;
Jian Li63430202018-08-30 16:24:09 +090098import java.io.ByteArrayInputStream;
99import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900100import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900101import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900102import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900103import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900104import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900105import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900106import java.util.HashMap;
107import java.util.Iterator;
108import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900109import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900110import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900111import java.util.Set;
112import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900113import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900114
115import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900116import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900117import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900118import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900119import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900120import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Daniel Parkc4d06402018-05-28 15:57:37 +0900121import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
122import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900123import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
124import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Daniel Parkec9d1132018-08-19 11:18:03 +0900125import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700126import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900127import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900128
129/**
130 * An utility that used in openstack networking app.
131 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900132public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900133
Daniel Park95985382018-07-23 11:38:07 +0900134 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900135
Daniel Parkc4d06402018-05-28 15:57:37 +0900136 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900137 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900138 private static final String PREFIX_DEVICE_NUMBER = "s";
139 private static final String PREFIX_FUNCTION_NUMBER = "f";
140
Jian Li51b844c2018-05-31 10:59:03 +0900141 // keystone endpoint related variables
142 private static final String DOMAIN_DEFAULT = "default";
143 private static final String KEYSTONE_V2 = "v2.0";
144 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900145 private static final String SSL_TYPE = "SSL";
146
Jian Li7f024de2018-07-07 03:51:02 +0900147 private static final String PROXY_MODE = "proxy";
148 private static final String BROADCAST_MODE = "broadcast";
149
Jian Licad36c72018-09-13 17:44:54 +0900150 private static final String ENABLE = "enable";
151 private static final String DISABLE = "disable";
152
Jian Li63430202018-08-30 16:24:09 +0900153 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
154
155 private static final String HMAC_SHA256 = "HmacSHA256";
156
Jian Li24ec59f2018-05-23 19:01:25 +0900157 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
158
Daniel Parka73c2362018-09-17 17:43:25 +0900159 private static final String VXLAN = "VXLAN";
Jian Li2d68c192018-12-13 15:52:59 +0900160 private static final String GRE = "GRE";
Daniel Parka73c2362018-09-17 17:43:25 +0900161 private static final String VLAN = "VLAN";
162 private static final String DL_DST = "dl_dst=";
163 private static final String NW_DST = "nw_dst=";
164 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
165 private static final String IN_PORT = "in_port=";
166 private static final String NW_SRC = "nw_src=";
167 private static final String COMMA = ",";
168 private static final String TUN_ID = "tun_id=";
169
170 private static final long TIMEOUT_MS = 5000;
171 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
172 private static final int SSH_PORT = 22;
173
Jian Li091d8d22018-02-20 10:42:06 +0900174 /**
175 * Prevents object instantiation from external.
176 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900177 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900178 }
179
180 /**
181 * Interprets JSON string to corresponding openstack model entity object.
182 *
183 * @param input JSON string
184 * @param entityClazz openstack model entity class
185 * @return openstack model entity object
186 */
187 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
188 ObjectMapper mapper = new ObjectMapper();
189 try {
190 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
191 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
192 return ObjectMapperSingleton.getContext(entityClazz)
193 .readerFor(entityClazz)
194 .readValue(jsonTree);
195 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900196 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900197 throw new IllegalArgumentException();
198 }
199 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900200
201 /**
202 * Converts openstack model entity object into JSON object.
203 *
204 * @param entity openstack model entity object
205 * @param entityClazz openstack model entity class
206 * @return JSON object
207 */
208 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
209 ObjectMapper mapper = new ObjectMapper();
210 try {
211 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
212 .writerFor(entityClazz)
213 .writeValueAsString(entity);
214 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900215 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900216 } catch (IOException e) {
217 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900218 throw new IllegalStateException();
219 }
220 }
Jian Li1064e4f2018-05-29 16:16:53 +0900221
222 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900223 * Obtains a floating IP associated with the given instance port.
224 *
225 * @param port instance port
226 * @param fips a collection of floating IPs
227 * @return associated floating IP
228 */
229 public static NetFloatingIP associatedFloatingIp(InstancePort port,
230 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900231 for (NetFloatingIP fip : fips) {
232 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
233 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900234 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900235 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
236 continue;
237 }
Jian Li6bc29d92018-10-02 13:55:05 +0900238 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
239 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900240 return fip;
241 }
Jian Li24ec59f2018-05-23 19:01:25 +0900242 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900243
Jian Li24ec59f2018-05-23 19:01:25 +0900244 return null;
245 }
246
247 /**
248 * Checks whether the given floating IP is associated with a VM.
249 *
250 * @param service openstack network service
251 * @param fip floating IP
252 * @return true if the given floating IP associated with a VM, false otherwise
253 */
254 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
255 NetFloatingIP fip) {
256 Port osPort = service.port(fip.getPortId());
257 if (osPort == null) {
258 return false;
259 }
260
261 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
262 Network osNet = service.network(osPort.getNetworkId());
263 if (osNet == null) {
264 final String errorFormat = ERR_FLOW + "no network(%s) exists";
265 final String error = String.format(errorFormat,
266 fip.getFloatingIpAddress(), osPort.getNetworkId());
267 throw new IllegalStateException(error);
268 }
269 return true;
270 } else {
271 return false;
272 }
273 }
274
275 /**
Jian Lia171a432018-06-11 11:52:11 +0900276 * Obtains the gateway node by instance port.
277 *
278 * @param gateways a collection of gateway nodes
279 * @param instPort instance port
280 * @return a gateway node
281 */
282 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
283 InstancePort instPort) {
284 OpenstackNode gw = null;
285 if (instPort != null && instPort.deviceId() != null) {
286 gw = getGwByComputeDevId(gateways, instPort.deviceId());
287 }
288 return gw;
289 }
290
291 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900292 * Obtains the gateway node by device in compute node. Note that the gateway
293 * node is determined by device's device identifier.
294 *
295 * @param gws a collection of gateway nodes
296 * @param deviceId device identifier
297 * @return a gateway node
298 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900299 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
300 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900301 int numOfGw = gws.size();
302
303 if (numOfGw == 0) {
304 return null;
305 }
306
307 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
308
309 return getGwByIndex(gws, gwIndex);
310 }
311
Jian Li51b844c2018-05-31 10:59:03 +0900312 /**
313 * Obtains a connected openstack client.
314 *
315 * @param osNode openstack node
316 * @return a connected openstack client
317 */
318 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900319 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900320 String endpoint = buildEndpoint(osNode);
321 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900322
Jian Li51b844c2018-05-31 10:59:03 +0900323 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900324
Jian Li51b844c2018-05-31 10:59:03 +0900325 try {
326 if (endpoint.contains(KEYSTONE_V2)) {
327 IOSClientBuilder.V2 builder = OSFactory.builderV2()
328 .endpoint(endpoint)
329 .tenantName(auth.project())
330 .credentials(auth.username(), auth.password())
331 .withConfig(config);
332
333 if (perspective != null) {
334 builder.perspective(getFacing(perspective));
335 }
336
337 return builder.authenticate();
338 } else if (endpoint.contains(KEYSTONE_V3)) {
339
340 Identifier project = Identifier.byName(auth.project());
341 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
342
343 IOSClientBuilder.V3 builder = OSFactory.builderV3()
344 .endpoint(endpoint)
345 .credentials(auth.username(), auth.password(), domain)
346 .scopeToProject(project, domain)
347 .withConfig(config);
348
349 if (perspective != null) {
350 builder.perspective(getFacing(perspective));
351 }
352
353 return builder.authenticate();
354 } else {
355 log.warn("Unrecognized keystone version type");
356 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900357 }
Jian Li51b844c2018-05-31 10:59:03 +0900358 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900359 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900360 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900361 }
Jian Li1064e4f2018-05-29 16:16:53 +0900362 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900363
364 /**
365 * Extract the interface name with the supplied port.
366 *
367 * @param port port
368 * @return interface name
369 */
370 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900371 String intfName;
372
Daniel Park95985382018-07-23 11:38:07 +0900373 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900374 log.error("Port profile is not found");
375 return null;
376 }
377
Daniel Park95985382018-07-23 11:38:07 +0900378 if (!port.getProfile().containsKey(PCISLOT) ||
379 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900380 log.error("Failed to retrieve the interface name because of no " +
381 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900382 return null;
383 }
Jian Li51b844c2018-05-31 10:59:03 +0900384
Daniel Parkff178ba2018-11-23 15:57:24 +0900385 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
386
387 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
388 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
389 return UNSUPPORTED_VENDOR;
390 }
391
392 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
393
Daniel Parkc4d06402018-05-28 15:57:37 +0900394 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
395 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
396
397 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
398 .split(":")[2]
399 .split("\\.")[0];
400 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
401
402 String functionNumHex = port.getProfile().get(PCISLOT).toString()
403 .split(":")[2]
404 .split("\\.")[1];
405 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
406
Daniel Parkc4d06402018-05-28 15:57:37 +0900407 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
408 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
409 } else {
410 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
411 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
412 }
413
414 return intfName;
415 }
Jian Li51b844c2018-05-31 10:59:03 +0900416
417 /**
Daniel Park95f73312018-07-31 15:48:34 +0900418 * Check if the given interface is added to the given device or not.
419 *
420 * @param deviceId device ID
421 * @param intfName interface name
422 * @param deviceService device service
423 * @return true if the given interface is added to the given device or false otherwise
424 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900425 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
426 String intfName,
427 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900428 checkNotNull(deviceId);
429 checkNotNull(intfName);
430
Jian Li5ecfd1a2018-12-10 11:41:03 +0900431 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
432 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900433 }
434
435 /**
Jian Li0b564282018-06-20 00:50:53 +0900436 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900437 *
438 * @param osPort port
439 * @param adminService openstack admin service
440 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900441 public static void addRouterIface(Port osPort,
442 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900443 osPort.getFixedIps().forEach(p -> {
444 JsonNode jsonTree = new ObjectMapper().createObjectNode()
445 .put("id", osPort.getDeviceId())
446 .put("tenant_id", osPort.getTenantId())
447 .put("subnet_id", p.getSubnetId())
448 .put("port_id", osPort.getId());
449 try {
450 RouterInterface rIface = getContext(NeutronRouterInterface.class)
451 .readerFor(NeutronRouterInterface.class)
452 .readValue(jsonTree);
453 if (adminService.routerInterface(rIface.getPortId()) != null) {
454 adminService.updateRouterInterface(rIface);
455 } else {
456 adminService.addRouterInterface(rIface);
457 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900458 } catch (IOException e) {
459 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900460 }
461 });
462 }
463
464 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900465 * Obtains the property value with specified property key name.
466 *
467 * @param properties a collection of properties
468 * @param name key name
469 * @return mapping value
470 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900471 public static String getPropertyValue(Set<ConfigProperty> properties,
472 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900473 Optional<ConfigProperty> property =
474 properties.stream().filter(p -> p.name().equals(name)).findFirst();
475 return property.map(ConfigProperty::value).orElse(null);
476 }
477
478 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900479 * Obtains the boolean property value with specified property key name.
480 *
481 * @param properties a collection of properties
482 * @param name key name
483 * @return mapping value
484 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900485 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
486 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900487 Optional<ConfigProperty> property =
488 properties.stream().filter(p -> p.name().equals(name)).findFirst();
489
490 return property.map(ConfigProperty::asBoolean).orElse(false);
491 }
492
493 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900494 * Prints out the JSON string in pretty format.
495 *
496 * @param mapper Object mapper
497 * @param jsonString JSON string
498 * @return pretty formatted JSON string
499 */
500 public static String prettyJson(ObjectMapper mapper, String jsonString) {
501 try {
502 Object jsonObject = mapper.readValue(jsonString, Object.class);
503 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900504 } catch (JsonParseException e) {
505 log.debug("JsonParseException caused by {}", e);
506 } catch (JsonMappingException e) {
507 log.debug("JsonMappingException caused by {}", e);
508 } catch (JsonProcessingException e) {
509 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900510 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900511 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900512 }
513 return null;
514 }
515
516 /**
Jian Li7f024de2018-07-07 03:51:02 +0900517 * Checks the validity of ARP mode.
518 *
519 * @param arpMode ARP mode
520 * @return returns true if the ARP mode is valid, false otherwise
521 */
522 public static boolean checkArpMode(String arpMode) {
523
524 if (isNullOrEmpty(arpMode)) {
525 return false;
526 } else {
527 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
528 }
529 }
530
531 /**
Jian Licad36c72018-09-13 17:44:54 +0900532 * Checks the validity of activation flag.
533 *
534 * @param activationFlag activation flag
535 * @return returns true if the activation flag is valid, false otherwise
536 */
537 public static boolean checkActivationFlag(String activationFlag) {
538
539 switch (activationFlag) {
540 case ENABLE:
541 return true;
542 case DISABLE:
543 return false;
544 default:
545 throw new IllegalArgumentException("The given activation flag is not valid!");
546 }
547 }
548
549 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900550 * Swaps current location with old location info.
551 * The revised instance port will be used to mod the flow rules after migration.
552 *
553 * @param instPort instance port
554 * @return location swapped instance port
555 */
556 public static InstancePort swapStaleLocation(InstancePort instPort) {
557 return DefaultInstancePort.builder()
558 .deviceId(instPort.oldDeviceId())
559 .portNumber(instPort.oldPortNumber())
560 .state(instPort.state())
561 .ipAddress(instPort.ipAddress())
562 .macAddress(instPort.macAddress())
563 .networkId(instPort.networkId())
564 .portId(instPort.portId())
565 .build();
566 }
567
568 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900569 * Compares two router interfaces are equal.
570 * Will be remove this after Openstack4j implements equals.
571 *
572 * @param routerInterface1 router interface
573 * @param routerInterface2 router interface
574 * @return returns true if two router interfaces are equal, false otherwise
575 */
Jian Li63430202018-08-30 16:24:09 +0900576 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
577 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900578 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
579 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
580 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
581 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
582 }
583
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900584 /**
585 * Returns the vnic type of given port.
586 *
587 * @param portName port name
588 * @return vnit type
589 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900590 public static VnicType vnicType(String portName) {
591 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
592 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
593 return VnicType.NORMAL;
594 } else if (isDirectPort(portName)) {
595 return VnicType.DIRECT;
596 } else {
597 return VnicType.UNSUPPORTED;
598 }
599 }
600
Jian Li63430202018-08-30 16:24:09 +0900601 /**
602 * Deserializes raw payload into HttpRequest object.
603 *
604 * @param rawData raw http payload
605 * @return HttpRequest object
606 */
607 public static HttpRequest parseHttpRequest(byte[] rawData) {
608 SessionInputBufferImpl sessionInputBuffer =
609 new SessionInputBufferImpl(
610 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
611 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900612 DefaultHttpRequestParser requestParser =
613 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900614 try {
615 return requestParser.parse();
616 } catch (IOException | HttpException e) {
617 log.warn("Failed to parse HttpRequest, due to {}", e);
618 }
619
620 return null;
621 }
622
623 /**
624 * Serializes HttpRequest object to byte array.
625 *
626 * @param request http request object
627 * @return byte array
628 */
629 public static byte[] unparseHttpRequest(HttpRequest request) {
630 try {
631 SessionOutputBufferImpl sessionOutputBuffer =
632 new SessionOutputBufferImpl(
633 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
634
635 ByteArrayOutputStream baos = new ByteArrayOutputStream();
636 sessionOutputBuffer.bind(baos);
637
Jian Li5ecfd1a2018-12-10 11:41:03 +0900638 HttpMessageWriter<HttpRequest> requestWriter =
639 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900640 requestWriter.write(request);
641 sessionOutputBuffer.flush();
642
643 return baos.toByteArray();
644 } catch (HttpException | IOException e) {
645 log.warn("Failed to unparse HttpRequest, due to {}", e);
646 }
647
648 return null;
649 }
650
651 /**
652 * Deserializes raw payload into HttpResponse object.
653 *
654 * @param rawData raw http payload
655 * @return HttpResponse object
656 */
657 public static HttpResponse parseHttpResponse(byte[] rawData) {
658 SessionInputBufferImpl sessionInputBuffer =
659 new SessionInputBufferImpl(
660 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
661 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900662 DefaultHttpResponseParser responseParser =
663 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900664 try {
665 return responseParser.parse();
666 } catch (IOException | HttpException e) {
667 log.warn("Failed to parse HttpResponse, due to {}", e);
668 }
669
670 return null;
671 }
672
673 /**
674 * Serializes HttpResponse header to byte array.
675 *
676 * @param response http response object
677 * @return byte array
678 */
679 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
680 try {
681 SessionOutputBufferImpl sessionOutputBuffer =
682 new SessionOutputBufferImpl(
683 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
684
685 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
686 sessionOutputBuffer.bind(headerBaos);
687
688 HttpMessageWriter<HttpResponse> responseWriter =
689 new DefaultHttpResponseWriter(sessionOutputBuffer);
690 responseWriter.write(response);
691 sessionOutputBuffer.flush();
692
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900693 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900694
695 return headerBaos.toByteArray();
696 } catch (IOException | HttpException e) {
697 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
698 }
699
700 return null;
701 }
702
703 /**
704 * Serializes HttpResponse object to byte array.
705 *
706 * @param response http response object
707 * @return byte array
708 */
709 public static byte[] unparseHttpResponseBody(HttpResponse response) {
710 try {
711 ByteArrayOutputStream baos = new ByteArrayOutputStream();
712 response.getEntity().writeTo(baos);
713
714 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900715 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900716
717 return baos.toByteArray();
718 } catch (IOException e) {
719 log.warn("Failed to unparse HttpResponse, due to {}", e);
720 }
721
722 return null;
723 }
724
725 /**
726 * Encodes the given data using HmacSHA256 encryption method with given secret key.
727 *
728 * @param key secret key
729 * @param data data to be encrypted
730 * @return Hmac256 encrypted data
731 */
732 public static String hmacEncrypt(String key, String data) {
733 try {
734 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
735 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
736 sha256Hmac.init(secretKey);
737 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
738 } catch (Exception e) {
739 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
740 }
741 return null;
742 }
743
Daniel Parka73c2362018-09-17 17:43:25 +0900744 /**
745 * Creates flow trace request string.
746 *
747 * @param srcIp src ip address
748 * @param dstIp dst ip address
749 * @param srcInstancePort src instance port
750 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900751 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900752 * @return flow trace request string
753 */
754 public static String traceRequestString(String srcIp,
755 String dstIp,
756 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900757 OpenstackNetworkService osNetService,
758 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900759
760 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
761
762 if (uplink) {
763
764 requestStringBuilder.append(COMMA)
765 .append(IN_PORT)
766 .append(srcInstancePort.portNumber().toString())
767 .append(COMMA)
768 .append(NW_SRC)
769 .append(srcIp)
770 .append(COMMA);
771
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900772 String modifiedDstIp = dstIp;
SONA Project6bc5c4a2018-12-14 23:49:52 +0900773 if (osNetService.networkType(srcInstancePort.networkId()) == Type.VXLAN ||
774 osNetService.networkType(srcInstancePort.networkId()) == Type.GRE ||
775 osNetService.networkType(srcInstancePort.networkId()) == Type.VLAN) {
Daniel Parka73c2362018-09-17 17:43:25 +0900776 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900777 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900778 requestStringBuilder.append(DL_DST)
779 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900780 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
781 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900782 requestStringBuilder.append(DL_DST)
783 .append(DEFAULT_GATEWAY_MAC_STR)
784 .append(COMMA);
785 }
786 } else {
787 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900788 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900789 }
790 }
791
792 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900793 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900794 .append("\n");
795 } else {
796 requestStringBuilder.append(COMMA)
797 .append(NW_SRC)
798 .append(dstIp)
799 .append(COMMA);
800
SONA Project6bc5c4a2018-12-14 23:49:52 +0900801 if (osNetService.networkType(srcInstancePort.networkId()) == Type.VXLAN ||
802 osNetService.networkType(srcInstancePort.networkId()) == Type.GRE ||
803 osNetService.networkType(srcInstancePort.networkId()) == Type.VLAN) {
Daniel Parka73c2362018-09-17 17:43:25 +0900804 requestStringBuilder.append(TUN_ID)
805 .append(osNetService.segmentId(srcInstancePort.networkId()))
806 .append(COMMA);
807 }
808 requestStringBuilder.append(NW_DST)
809 .append(srcIp)
810 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900811 }
812
813 return requestStringBuilder.toString();
814 }
815
816 /**
817 * Sends flow trace string to node.
818 *
819 * @param requestString reqeust string
820 * @param node src node
821 * @return flow trace result in string format
822 */
823 public static String sendTraceRequestToNode(String requestString,
824 OpenstackNode node) {
825 String traceResult = null;
826 OpenstackSshAuth sshAuth = node.sshAuthInfo();
827
828 try (SshClient client = SshClient.setUpDefaultClient()) {
829 client.start();
830
831 try (ClientSession session = client
832 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
833 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
834 session.addPasswordIdentity(sshAuth.password());
835 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
836
837
838 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
839
840 log.debug("requestString: {}", requestString);
841 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900842 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900843
Jian Li5ecfd1a2018-12-10 11:41:03 +0900844 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900845 OutputStream errStream = new ByteArrayOutputStream();
846
847 channel.setIn(new NoCloseInputStream(inputStream));
848 channel.setErr(errStream);
849 channel.setOut(outputStream);
850
851 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
852 eventList.add(ClientChannelEvent.OPENED);
853
854 OpenFuture channelFuture = channel.open();
855
856 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
857
858 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
859
860 while (!channelFuture.isOpened()) {
861 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
862 log.error("Failed to open channel");
863 return null;
864 }
865 }
866 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
867
Jian Li5ecfd1a2018-12-10 11:41:03 +0900868 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900869
870 channel.close();
871 }
872 } finally {
873 session.close();
874 }
875 } finally {
876 client.stop();
877 }
878
879 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900880 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900881 }
882
883 return traceResult;
884 }
885
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900886 /**
887 * Returns the floating ip with supplied instance port.
888 *
889 * @param instancePort instance port
890 * @param osRouterAdminService openstack router admin service
891 * @return floating ip
892 */
893 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900894 OpenstackRouterAdminService
895 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900896 return osRouterAdminService.floatingIps().stream()
897 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
898 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
899 .findAny().orElse(null);
900 }
901
902 /**
903 * Sends GARP packet with supplied floating ip information.
904 *
905 * @param floatingIP floating ip
906 * @param instancePort instance port
907 * @param vlanId vlain id
908 * @param gatewayNode gateway node
909 * @param packetService packet service
910 */
Jian Li32b03622018-11-06 17:54:24 +0900911 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
912 InstancePort instancePort,
913 VlanId vlanId,
914 OpenstackNode gatewayNode,
915 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900916 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
917
918 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
919 .setOutput(gatewayNode.uplinkPortNum()).build();
920
921 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
922 ByteBuffer.wrap(ethernet.serialize())));
923 }
924
925 /**
926 * Returns the external peer router with supplied network information.
927 *
928 * @param network network
929 * @param osNetworkService openstack network service
930 * @param osRouterAdminService openstack router admin service
931 * @return external peer router
932 */
933 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900934 OpenstackNetworkService
935 osNetworkService,
936 OpenstackRouterAdminService
937 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900938 if (network == null) {
939 return null;
940 }
941
942 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
943
944 if (subnet == null) {
945 return null;
946 }
947
948 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
949 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
950 .findAny().orElse(null);
951 if (osRouterIface == null) {
952 return null;
953 }
954
955 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900956 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900957 return null;
958 }
959
960 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
961 return osNetworkService.externalPeerRouter(exGatewayInfo);
962
963 }
964
Jian Liebde74d2018-11-14 00:18:57 +0900965 /**
966 * Returns the external peer router with specified subnet information.
967 *
968 * @param subnet openstack subnet
969 * @param osRouterService openstack router service
970 * @param osNetworkService openstack network service
971 * @return external peer router
972 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900973 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
974 OpenstackRouterService
975 osRouterService,
976 OpenstackNetworkService
977 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +0900978 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
979 if (osRouter == null) {
980 return null;
981 }
982 if (osRouter.getExternalGatewayInfo() == null) {
983 // this router does not have external connectivity
984 log.trace("router({}) has no external gateway",
985 osRouter.getName());
986 return null;
987 }
988
989 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
990 }
991
992 /**
993 * Returns the external ip address with specified router information.
994 *
995 * @param srcSubnet source subnet
996 * @param osRouterService openstack router service
997 * @param osNetworkService openstack network service
998 * @return external ip address
999 */
1000 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001001 OpenstackRouterService
1002 osRouterService,
1003 OpenstackNetworkService
1004 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001005
1006 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1007
1008 if (osRouter.getExternalGatewayInfo() == null) {
1009 // this router does not have external connectivity
1010 log.trace("router({}) has no external gateway",
1011 osRouter.getName());
1012 return null;
1013 }
1014
1015 return getExternalIp(osRouter, osNetworkService);
1016 }
1017
1018 /**
1019 * Returns the external ip address with specified router information.
1020 *
1021 * @param router openstack router
1022 * @param osNetworkService openstack network service
1023 * @return external ip address
1024 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001025 public static IpAddress getExternalIp(Router router,
1026 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001027 if (router == null) {
1028 return null;
1029 }
1030
1031 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1032 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001033 log.trace("Failed to get externalIp for router {} because " +
1034 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001035 router.getId());
1036 return null;
1037 }
1038
1039 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1040 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1041 .stream()
1042 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1043 .findAny().orElse(null);
1044
1045 if (exGatewayPort == null) {
1046 return null;
1047 }
1048
1049 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1050 .findAny().get().getIpAddress());
1051 }
1052
Jian Li2d68c192018-12-13 15:52:59 +09001053 /**
1054 * Returns the tunnel port number with specified net ID and openstack node.
1055 *
1056 * @param netId network ID
1057 * @param netService network service
1058 * @param osNode openstack node
1059 * @return tunnel port number
1060 */
1061 public static PortNumber tunnelPortNumByNetId(String netId,
1062 OpenstackNetworkService netService,
1063 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001064 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001065
1066 if (netType == null) {
1067 return null;
1068 }
1069
1070 return tunnelPortNumByNetType(netType, osNode);
1071 }
1072
1073 /**
1074 * Returns the tunnel port number with specified net type and openstack node.
1075 *
1076 * @param netType network type
1077 * @param osNode openstack node
1078 * @return tunnel port number
1079 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001080 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001081 switch (netType) {
1082 case VXLAN:
1083 return osNode.vxlanTunnelPortNum();
1084 case GRE:
1085 return osNode.greTunnelPortNum();
1086 default:
1087 return null;
1088 }
1089 }
1090
Jian Li5ecfd1a2018-12-10 11:41:03 +09001091 private static Router getRouterFromSubnet(Subnet subnet,
1092 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001093 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1094 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1095 .findAny().orElse(null);
1096 if (osRouterIface == null) {
1097 return null;
1098 }
1099
1100 return osRouterService.router(osRouterIface.getId());
1101 }
1102
Daniel Park7e8c4d82018-08-13 23:47:49 +09001103 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001104 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001105 }
1106
Daniel Park2ff66b42018-08-01 11:52:45 +09001107 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001108 * Returns GARP packet with supplied floating ip and instance port information.
1109 *
1110 * @param floatingIP floating ip
1111 * @param instancePort instance port
1112 * @param vlanId vlan id
1113 * @return GARP packet
1114 */
1115 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1116 InstancePort instancePort,
1117 VlanId vlanId) {
1118 Ethernet ethernet = new Ethernet();
1119 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1120 ethernet.setSourceMACAddress(instancePort.macAddress());
1121 ethernet.setEtherType(Ethernet.TYPE_ARP);
1122 ethernet.setVlanID(vlanId.id());
1123
1124 ARP arp = new ARP();
1125 arp.setOpCode(ARP.OP_REPLY);
1126 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1127 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1128
1129 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1130 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1131
1132 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1133 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1134
Jian Li5ecfd1a2018-12-10 11:41:03 +09001135 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1136 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001137
1138 ethernet.setPayload(arp);
1139
1140 return ethernet;
1141 }
1142
1143 /**
Jian Li51b844c2018-05-31 10:59:03 +09001144 * Builds up and a complete endpoint URL from gateway node.
1145 *
1146 * @param node gateway node
1147 * @return a complete endpoint URL
1148 */
1149 private static String buildEndpoint(OpenstackNode node) {
1150
Jian Lic704b672018-09-04 18:52:53 +09001151 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001152
1153 StringBuilder endpointSb = new StringBuilder();
1154 endpointSb.append(auth.protocol().name().toLowerCase());
1155 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001156 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001157 return endpointSb.toString();
1158 }
1159
1160 /**
1161 * Obtains the SSL config without verifying the certification.
1162 *
1163 * @return SSL config
1164 */
1165 private static Config getSslConfig() {
1166 // we bypass the SSL certification verification for now
1167 // TODO: verify server side SSL using a given certification
1168 Config config = Config.newConfig().withSSLVerificationDisabled();
1169
1170 TrustManager[] trustAllCerts = new TrustManager[]{
1171 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001172 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001173 public X509Certificate[] getAcceptedIssuers() {
1174 return null;
1175 }
1176
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001177 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001178 public void checkClientTrusted(X509Certificate[] certs,
1179 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001180 return;
Jian Li51b844c2018-05-31 10:59:03 +09001181 }
1182
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001183 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001184 public void checkServerTrusted(X509Certificate[] certs,
1185 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001186 return;
Jian Li51b844c2018-05-31 10:59:03 +09001187 }
1188 }
1189 };
1190
1191 HostnameVerifier allHostsValid = (hostname, session) -> true;
1192
1193 try {
1194 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1195 sc.init(null, trustAllCerts,
1196 new java.security.SecureRandom());
1197 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1198 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1199
1200 config.withSSLContext(sc);
1201 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001202 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001203 return null;
1204 }
1205
1206 return config;
1207 }
1208
1209 /**
1210 * Obtains the facing object with given openstack perspective.
1211 *
1212 * @param perspective keystone perspective
1213 * @return facing object
1214 */
1215 private static Facing getFacing(Perspective perspective) {
1216
1217 switch (perspective) {
1218 case PUBLIC:
1219 return Facing.PUBLIC;
1220 case ADMIN:
1221 return Facing.ADMIN;
1222 case INTERNAL:
1223 return Facing.INTERNAL;
1224 default:
1225 return null;
1226 }
1227 }
1228
1229 /**
1230 * Obtains gateway instance by giving index number.
1231 *
1232 * @param gws a collection of gateway nodes
1233 * @param index index number
1234 * @return gateway instance
1235 */
1236 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1237 Map<String, OpenstackNode> hashMap = new HashMap<>();
1238 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1239 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1240 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1241
1242 int intIndex = 0;
1243 OpenstackNode gw = null;
1244 while (iteratorKey.hasNext()) {
1245 String key = iteratorKey.next();
1246
1247 if (intIndex == index) {
1248 gw = treeMap.get(key);
1249 }
1250 intIndex++;
1251 }
1252 return gw;
1253 }
Jian Li63430202018-08-30 16:24:09 +09001254}