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