blob: 8837a650b4fc37f6241c7667dae64acc716420b9 [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;
Jian Li51728702019-05-17 18:38:56 +090028import org.apache.commons.lang3.StringUtils;
Jian Li63430202018-08-30 16:24:09 +090029import org.apache.http.HttpException;
30import org.apache.http.HttpRequest;
31import org.apache.http.HttpResponse;
32import org.apache.http.impl.io.DefaultHttpRequestParser;
33import org.apache.http.impl.io.DefaultHttpRequestWriter;
34import org.apache.http.impl.io.DefaultHttpResponseParser;
35import org.apache.http.impl.io.DefaultHttpResponseWriter;
36import org.apache.http.impl.io.HttpTransportMetricsImpl;
37import org.apache.http.impl.io.SessionInputBufferImpl;
38import org.apache.http.impl.io.SessionOutputBufferImpl;
39import org.apache.http.io.HttpMessageWriter;
Daniel Parka73c2362018-09-17 17:43:25 +090040import org.apache.sshd.client.SshClient;
41import org.apache.sshd.client.channel.ClientChannel;
42import org.apache.sshd.client.channel.ClientChannelEvent;
43import org.apache.sshd.client.future.OpenFuture;
44import org.apache.sshd.client.session.ClientSession;
45import org.apache.sshd.common.util.io.NoCloseInputStream;
Jian Li7b8c3682019-05-12 13:57:15 +090046import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090047import org.onlab.packet.ARP;
48import org.onlab.packet.Ethernet;
49import org.onlab.packet.Ip4Address;
Daniel Parka73c2362018-09-17 17:43:25 +090050import org.onlab.packet.IpAddress;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090051import org.onlab.packet.MacAddress;
52import org.onlab.packet.VlanId;
Jian Li7f70bb72018-07-06 23:35:30 +090053import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090054import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090055import org.onosproject.net.PortNumber;
Daniel Park95f73312018-07-31 15:48:34 +090056import org.onosproject.net.device.DeviceService;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090057import org.onosproject.net.flow.DefaultTrafficTreatment;
58import org.onosproject.net.flow.TrafficTreatment;
Jian Lid5727622019-09-11 11:15:16 +090059import org.onosproject.net.group.DefaultGroupKey;
60import org.onosproject.net.group.GroupKey;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090061import org.onosproject.net.packet.DefaultOutboundPacket;
62import org.onosproject.net.packet.PacketService;
Daniel Park7e8c4d82018-08-13 23:47:49 +090063import org.onosproject.openstacknetworking.api.Constants.VnicType;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090064import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Jian Lia171a432018-06-11 11:52:11 +090065import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li7b8c3682019-05-12 13:57:15 +090066import org.onosproject.openstacknetworking.api.OpenstackHaService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090067import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Li24ec59f2018-05-23 19:01:25 +090068import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090069import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liebde74d2018-11-14 00:18:57 +090070import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Liec5c32b2018-07-13 14:28:58 +090071import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090072import org.onosproject.openstacknode.api.OpenstackAuth;
73import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090074import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Parka73c2362018-09-17 17:43:25 +090075import org.onosproject.openstacknode.api.OpenstackSshAuth;
Jian Li51728702019-05-17 18:38:56 +090076import org.onosproject.ovsdb.controller.OvsdbClientService;
77import org.onosproject.ovsdb.controller.OvsdbController;
78import org.onosproject.ovsdb.controller.OvsdbNodeId;
Jian Li51b844c2018-05-31 10:59:03 +090079import org.openstack4j.api.OSClient;
80import org.openstack4j.api.client.IOSClientBuilder;
81import org.openstack4j.api.exceptions.AuthenticationException;
82import org.openstack4j.api.types.Facing;
83import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090084import org.openstack4j.core.transport.ObjectMapperSingleton;
85import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090086import org.openstack4j.model.common.Identifier;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090087import org.openstack4j.model.network.ExternalGateway;
Jian Li24ec59f2018-05-23 19:01:25 +090088import org.openstack4j.model.network.NetFloatingIP;
89import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090090import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090091import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090092import org.openstack4j.model.network.RouterInterface;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090093import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090094import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090095import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090096import org.slf4j.Logger;
97import org.slf4j.LoggerFactory;
98
Jian Li63430202018-08-30 16:24:09 +090099import javax.crypto.Mac;
100import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +0900101import javax.net.ssl.HostnameVerifier;
102import javax.net.ssl.HttpsURLConnection;
103import javax.net.ssl.SSLContext;
104import javax.net.ssl.TrustManager;
105import javax.net.ssl.X509TrustManager;
Jian Li7b8c3682019-05-12 13:57:15 +0900106import javax.ws.rs.client.Client;
107import javax.ws.rs.client.ClientBuilder;
108import javax.ws.rs.client.Entity;
109import javax.ws.rs.client.WebTarget;
110import javax.ws.rs.core.Response;
Jian Li63430202018-08-30 16:24:09 +0900111import java.io.ByteArrayInputStream;
112import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900113import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900114import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900115import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900116import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900117import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900118import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900119import java.util.HashMap;
120import java.util.Iterator;
121import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900122import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900123import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900124import java.util.Set;
125import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900126import java.util.concurrent.TimeUnit;
Jian Li091d8d22018-02-20 10:42:06 +0900127
128import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900129import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900130import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li7b8c3682019-05-12 13:57:15 +0900131import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
132import static org.apache.commons.io.IOUtils.toInputStream;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900133import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900134import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900135import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li7b8c3682019-05-12 13:57:15 +0900136import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_REST_PATH;
Daniel Parkc4d06402018-05-28 15:57:37 +0900137import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
138import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900139import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
140import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Jian Li7b8c3682019-05-12 13:57:15 +0900141import static org.onosproject.openstacknetworking.api.Constants.REST_PASSWORD;
142import static org.onosproject.openstacknetworking.api.Constants.REST_PORT;
143import static org.onosproject.openstacknetworking.api.Constants.REST_USER;
144import static org.onosproject.openstacknetworking.api.Constants.REST_UTF8;
Daniel Parkec9d1132018-08-19 11:18:03 +0900145import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700146import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900147import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900148
149/**
150 * An utility that used in openstack networking app.
151 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900152public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900153
Daniel Park95985382018-07-23 11:38:07 +0900154 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900155
Daniel Parkc4d06402018-05-28 15:57:37 +0900156 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900157 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900158 private static final String PREFIX_DEVICE_NUMBER = "s";
159 private static final String PREFIX_FUNCTION_NUMBER = "f";
160
Jian Li51b844c2018-05-31 10:59:03 +0900161 // keystone endpoint related variables
162 private static final String DOMAIN_DEFAULT = "default";
163 private static final String KEYSTONE_V2 = "v2.0";
164 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900165 private static final String SSL_TYPE = "SSL";
166
Jian Li7f024de2018-07-07 03:51:02 +0900167 private static final String PROXY_MODE = "proxy";
168 private static final String BROADCAST_MODE = "broadcast";
169
Jian Licad36c72018-09-13 17:44:54 +0900170 private static final String ENABLE = "enable";
171 private static final String DISABLE = "disable";
172
Jian Li63430202018-08-30 16:24:09 +0900173 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
174
175 private static final String HMAC_SHA256 = "HmacSHA256";
176
Jian Li24ec59f2018-05-23 19:01:25 +0900177 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
178
Daniel Parka73c2362018-09-17 17:43:25 +0900179 private static final String DL_DST = "dl_dst=";
180 private static final String NW_DST = "nw_dst=";
181 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
182 private static final String IN_PORT = "in_port=";
183 private static final String NW_SRC = "nw_src=";
184 private static final String COMMA = ",";
185 private static final String TUN_ID = "tun_id=";
186
187 private static final long TIMEOUT_MS = 5000;
188 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
189 private static final int SSH_PORT = 22;
190
Jian Li51728702019-05-17 18:38:56 +0900191 private static final int TAP_PORT_LENGTH = 11;
Jian Lia271b3c2019-09-03 23:10:20 +0900192 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li51728702019-05-17 18:38:56 +0900193
Jian Li091d8d22018-02-20 10:42:06 +0900194 /**
195 * Prevents object instantiation from external.
196 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900197 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900198 }
199
200 /**
201 * Interprets JSON string to corresponding openstack model entity object.
202 *
Jian Li7b8c3682019-05-12 13:57:15 +0900203 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900204 * @param entityClazz openstack model entity class
205 * @return openstack model entity object
206 */
Jian Li7b8c3682019-05-12 13:57:15 +0900207 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900208 ObjectMapper mapper = new ObjectMapper();
209 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900210 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900211 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
212 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
213 return ObjectMapperSingleton.getContext(entityClazz)
214 .readerFor(entityClazz)
215 .readValue(jsonTree);
216 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900217 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900218 throw new IllegalArgumentException();
219 }
220 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900221
222 /**
223 * Converts openstack model entity object into JSON object.
224 *
225 * @param entity openstack model entity object
226 * @param entityClazz openstack model entity class
227 * @return JSON object
228 */
229 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
230 ObjectMapper mapper = new ObjectMapper();
231 try {
232 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
233 .writerFor(entityClazz)
234 .writeValueAsString(entity);
235 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900236 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900237 } catch (IOException e) {
238 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900239 throw new IllegalStateException();
240 }
241 }
Jian Li1064e4f2018-05-29 16:16:53 +0900242
243 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900244 * Obtains a floating IP associated with the given instance port.
245 *
246 * @param port instance port
247 * @param fips a collection of floating IPs
248 * @return associated floating IP
249 */
250 public static NetFloatingIP associatedFloatingIp(InstancePort port,
251 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900252 for (NetFloatingIP fip : fips) {
253 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
254 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900255 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900256 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
257 continue;
258 }
Jian Li6bc29d92018-10-02 13:55:05 +0900259 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
260 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900261 return fip;
262 }
Jian Li24ec59f2018-05-23 19:01:25 +0900263 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900264
Jian Li24ec59f2018-05-23 19:01:25 +0900265 return null;
266 }
267
268 /**
269 * Checks whether the given floating IP is associated with a VM.
270 *
271 * @param service openstack network service
272 * @param fip floating IP
273 * @return true if the given floating IP associated with a VM, false otherwise
274 */
275 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
276 NetFloatingIP fip) {
277 Port osPort = service.port(fip.getPortId());
278 if (osPort == null) {
279 return false;
280 }
281
282 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
283 Network osNet = service.network(osPort.getNetworkId());
284 if (osNet == null) {
285 final String errorFormat = ERR_FLOW + "no network(%s) exists";
286 final String error = String.format(errorFormat,
287 fip.getFloatingIpAddress(), osPort.getNetworkId());
288 throw new IllegalStateException(error);
289 }
290 return true;
291 } else {
292 return false;
293 }
294 }
295
296 /**
Jian Lia171a432018-06-11 11:52:11 +0900297 * Obtains the gateway node by instance port.
298 *
299 * @param gateways a collection of gateway nodes
300 * @param instPort instance port
301 * @return a gateway node
302 */
303 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
304 InstancePort instPort) {
305 OpenstackNode gw = null;
306 if (instPort != null && instPort.deviceId() != null) {
307 gw = getGwByComputeDevId(gateways, instPort.deviceId());
308 }
309 return gw;
310 }
311
312 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900313 * Obtains the gateway node by device in compute node. Note that the gateway
314 * node is determined by device's device identifier.
315 *
316 * @param gws a collection of gateway nodes
317 * @param deviceId device identifier
318 * @return a gateway node
319 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900320 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
321 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900322 int numOfGw = gws.size();
323
324 if (numOfGw == 0) {
325 return null;
326 }
327
328 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
329
330 return getGwByIndex(gws, gwIndex);
331 }
332
Jian Li51b844c2018-05-31 10:59:03 +0900333 /**
334 * Obtains a connected openstack client.
335 *
336 * @param osNode openstack node
337 * @return a connected openstack client
338 */
339 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900340 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900341 String endpoint = buildEndpoint(osNode);
342 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900343
Jian Li51b844c2018-05-31 10:59:03 +0900344 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900345
Jian Li51b844c2018-05-31 10:59:03 +0900346 try {
347 if (endpoint.contains(KEYSTONE_V2)) {
348 IOSClientBuilder.V2 builder = OSFactory.builderV2()
349 .endpoint(endpoint)
350 .tenantName(auth.project())
351 .credentials(auth.username(), auth.password())
352 .withConfig(config);
353
354 if (perspective != null) {
355 builder.perspective(getFacing(perspective));
356 }
357
358 return builder.authenticate();
359 } else if (endpoint.contains(KEYSTONE_V3)) {
360
361 Identifier project = Identifier.byName(auth.project());
362 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
363
364 IOSClientBuilder.V3 builder = OSFactory.builderV3()
365 .endpoint(endpoint)
366 .credentials(auth.username(), auth.password(), domain)
367 .scopeToProject(project, domain)
368 .withConfig(config);
369
370 if (perspective != null) {
371 builder.perspective(getFacing(perspective));
372 }
373
374 return builder.authenticate();
375 } else {
376 log.warn("Unrecognized keystone version type");
377 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900378 }
Jian Li51b844c2018-05-31 10:59:03 +0900379 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900380 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900381 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900382 }
Jian Li1064e4f2018-05-29 16:16:53 +0900383 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900384
385 /**
386 * Extract the interface name with the supplied port.
387 *
388 * @param port port
389 * @return interface name
390 */
391 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900392 String intfName;
393
Daniel Park95985382018-07-23 11:38:07 +0900394 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900395 log.error("Port profile is not found");
396 return null;
397 }
398
Daniel Park95985382018-07-23 11:38:07 +0900399 if (!port.getProfile().containsKey(PCISLOT) ||
400 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900401 log.error("Failed to retrieve the interface name because of no " +
402 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900403 return null;
404 }
Jian Li51b844c2018-05-31 10:59:03 +0900405
Daniel Parkff178ba2018-11-23 15:57:24 +0900406 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
407
408 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
409 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
410 return UNSUPPORTED_VENDOR;
411 }
412
413 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
414
Daniel Parkc4d06402018-05-28 15:57:37 +0900415 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
416 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
417
418 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
419 .split(":")[2]
420 .split("\\.")[0];
421 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
422
423 String functionNumHex = port.getProfile().get(PCISLOT).toString()
424 .split(":")[2]
425 .split("\\.")[1];
426 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
427
Daniel Parkc4d06402018-05-28 15:57:37 +0900428 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
429 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
430 } else {
431 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
432 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
433 }
434
435 return intfName;
436 }
Jian Li51b844c2018-05-31 10:59:03 +0900437
438 /**
Daniel Park95f73312018-07-31 15:48:34 +0900439 * Check if the given interface is added to the given device or not.
440 *
441 * @param deviceId device ID
442 * @param intfName interface name
443 * @param deviceService device service
444 * @return true if the given interface is added to the given device or false otherwise
445 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900446 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
447 String intfName,
448 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900449 checkNotNull(deviceId);
450 checkNotNull(intfName);
451
Jian Li5ecfd1a2018-12-10 11:41:03 +0900452 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
453 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900454 }
455
456 /**
Jian Li0b564282018-06-20 00:50:53 +0900457 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900458 *
459 * @param osPort port
460 * @param adminService openstack admin service
461 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900462 public static void addRouterIface(Port osPort,
463 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900464 osPort.getFixedIps().forEach(p -> {
465 JsonNode jsonTree = new ObjectMapper().createObjectNode()
466 .put("id", osPort.getDeviceId())
467 .put("tenant_id", osPort.getTenantId())
468 .put("subnet_id", p.getSubnetId())
469 .put("port_id", osPort.getId());
470 try {
471 RouterInterface rIface = getContext(NeutronRouterInterface.class)
472 .readerFor(NeutronRouterInterface.class)
473 .readValue(jsonTree);
474 if (adminService.routerInterface(rIface.getPortId()) != null) {
475 adminService.updateRouterInterface(rIface);
476 } else {
477 adminService.addRouterInterface(rIface);
478 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900479 } catch (IOException e) {
480 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900481 }
482 });
483 }
484
485 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900486 * Obtains the property value with specified property key name.
487 *
488 * @param properties a collection of properties
489 * @param name key name
490 * @return mapping value
491 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900492 public static String getPropertyValue(Set<ConfigProperty> properties,
493 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900494 Optional<ConfigProperty> property =
495 properties.stream().filter(p -> p.name().equals(name)).findFirst();
496 return property.map(ConfigProperty::value).orElse(null);
497 }
498
499 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900500 * Obtains the boolean property value with specified property key name.
501 *
502 * @param properties a collection of properties
503 * @param name key name
504 * @return mapping value
505 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900506 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
507 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900508 Optional<ConfigProperty> property =
509 properties.stream().filter(p -> p.name().equals(name)).findFirst();
510
511 return property.map(ConfigProperty::asBoolean).orElse(false);
512 }
513
514 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900515 * Prints out the JSON string in pretty format.
516 *
517 * @param mapper Object mapper
518 * @param jsonString JSON string
519 * @return pretty formatted JSON string
520 */
521 public static String prettyJson(ObjectMapper mapper, String jsonString) {
522 try {
523 Object jsonObject = mapper.readValue(jsonString, Object.class);
524 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900525 } catch (JsonParseException e) {
526 log.debug("JsonParseException caused by {}", e);
527 } catch (JsonMappingException e) {
528 log.debug("JsonMappingException caused by {}", e);
529 } catch (JsonProcessingException e) {
530 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900531 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900532 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900533 }
534 return null;
535 }
536
537 /**
Jian Li7f024de2018-07-07 03:51:02 +0900538 * Checks the validity of ARP mode.
539 *
540 * @param arpMode ARP mode
541 * @return returns true if the ARP mode is valid, false otherwise
542 */
543 public static boolean checkArpMode(String arpMode) {
544
545 if (isNullOrEmpty(arpMode)) {
546 return false;
547 } else {
548 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
549 }
550 }
551
552 /**
Jian Licad36c72018-09-13 17:44:54 +0900553 * Checks the validity of activation flag.
554 *
555 * @param activationFlag activation flag
556 * @return returns true if the activation flag is valid, false otherwise
557 */
558 public static boolean checkActivationFlag(String activationFlag) {
559
560 switch (activationFlag) {
561 case ENABLE:
562 return true;
563 case DISABLE:
564 return false;
565 default:
566 throw new IllegalArgumentException("The given activation flag is not valid!");
567 }
568 }
569
570 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900571 * Swaps current location with old location info.
572 * The revised instance port will be used to mod the flow rules after migration.
573 *
574 * @param instPort instance port
575 * @return location swapped instance port
576 */
577 public static InstancePort swapStaleLocation(InstancePort instPort) {
578 return DefaultInstancePort.builder()
579 .deviceId(instPort.oldDeviceId())
580 .portNumber(instPort.oldPortNumber())
581 .state(instPort.state())
582 .ipAddress(instPort.ipAddress())
583 .macAddress(instPort.macAddress())
584 .networkId(instPort.networkId())
585 .portId(instPort.portId())
586 .build();
587 }
588
589 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900590 * Compares two router interfaces are equal.
591 * Will be remove this after Openstack4j implements equals.
592 *
593 * @param routerInterface1 router interface
594 * @param routerInterface2 router interface
595 * @return returns true if two router interfaces are equal, false otherwise
596 */
Jian Li63430202018-08-30 16:24:09 +0900597 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
598 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900599 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
600 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
601 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
602 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
603 }
604
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900605 /**
606 * Returns the vnic type of given port.
607 *
608 * @param portName port name
609 * @return vnit type
610 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900611 public static VnicType vnicType(String portName) {
612 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
613 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
614 return VnicType.NORMAL;
615 } else if (isDirectPort(portName)) {
616 return VnicType.DIRECT;
617 } else {
618 return VnicType.UNSUPPORTED;
619 }
620 }
621
Jian Li63430202018-08-30 16:24:09 +0900622 /**
623 * Deserializes raw payload into HttpRequest object.
624 *
625 * @param rawData raw http payload
626 * @return HttpRequest object
627 */
628 public static HttpRequest parseHttpRequest(byte[] rawData) {
629 SessionInputBufferImpl sessionInputBuffer =
630 new SessionInputBufferImpl(
631 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
632 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900633 DefaultHttpRequestParser requestParser =
634 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900635 try {
636 return requestParser.parse();
637 } catch (IOException | HttpException e) {
638 log.warn("Failed to parse HttpRequest, due to {}", e);
639 }
640
641 return null;
642 }
643
644 /**
645 * Serializes HttpRequest object to byte array.
646 *
647 * @param request http request object
648 * @return byte array
649 */
650 public static byte[] unparseHttpRequest(HttpRequest request) {
651 try {
652 SessionOutputBufferImpl sessionOutputBuffer =
653 new SessionOutputBufferImpl(
654 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
655
656 ByteArrayOutputStream baos = new ByteArrayOutputStream();
657 sessionOutputBuffer.bind(baos);
658
Jian Li5ecfd1a2018-12-10 11:41:03 +0900659 HttpMessageWriter<HttpRequest> requestWriter =
660 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900661 requestWriter.write(request);
662 sessionOutputBuffer.flush();
663
664 return baos.toByteArray();
665 } catch (HttpException | IOException e) {
666 log.warn("Failed to unparse HttpRequest, due to {}", e);
667 }
668
669 return null;
670 }
671
672 /**
673 * Deserializes raw payload into HttpResponse object.
674 *
675 * @param rawData raw http payload
676 * @return HttpResponse object
677 */
678 public static HttpResponse parseHttpResponse(byte[] rawData) {
679 SessionInputBufferImpl sessionInputBuffer =
680 new SessionInputBufferImpl(
681 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
682 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900683 DefaultHttpResponseParser responseParser =
684 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900685 try {
686 return responseParser.parse();
687 } catch (IOException | HttpException e) {
688 log.warn("Failed to parse HttpResponse, due to {}", e);
689 }
690
691 return null;
692 }
693
694 /**
695 * Serializes HttpResponse header to byte array.
696 *
697 * @param response http response object
698 * @return byte array
699 */
700 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
701 try {
702 SessionOutputBufferImpl sessionOutputBuffer =
703 new SessionOutputBufferImpl(
704 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
705
706 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
707 sessionOutputBuffer.bind(headerBaos);
708
709 HttpMessageWriter<HttpResponse> responseWriter =
710 new DefaultHttpResponseWriter(sessionOutputBuffer);
711 responseWriter.write(response);
712 sessionOutputBuffer.flush();
713
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900714 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900715
716 return headerBaos.toByteArray();
717 } catch (IOException | HttpException e) {
718 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
719 }
720
721 return null;
722 }
723
724 /**
725 * Serializes HttpResponse object to byte array.
726 *
727 * @param response http response object
728 * @return byte array
729 */
730 public static byte[] unparseHttpResponseBody(HttpResponse response) {
731 try {
732 ByteArrayOutputStream baos = new ByteArrayOutputStream();
733 response.getEntity().writeTo(baos);
734
735 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900736 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900737
738 return baos.toByteArray();
739 } catch (IOException e) {
740 log.warn("Failed to unparse HttpResponse, due to {}", e);
741 }
742
743 return null;
744 }
745
746 /**
747 * Encodes the given data using HmacSHA256 encryption method with given secret key.
748 *
749 * @param key secret key
750 * @param data data to be encrypted
751 * @return Hmac256 encrypted data
752 */
753 public static String hmacEncrypt(String key, String data) {
754 try {
755 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
756 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
757 sha256Hmac.init(secretKey);
758 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
759 } catch (Exception e) {
760 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
761 }
762 return null;
763 }
764
Daniel Parka73c2362018-09-17 17:43:25 +0900765 /**
766 * Creates flow trace request string.
767 *
768 * @param srcIp src ip address
769 * @param dstIp dst ip address
770 * @param srcInstancePort src instance port
771 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900772 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900773 * @return flow trace request string
774 */
775 public static String traceRequestString(String srcIp,
776 String dstIp,
777 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900778 OpenstackNetworkService osNetService,
779 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900780
781 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
782
783 if (uplink) {
784
785 requestStringBuilder.append(COMMA)
786 .append(IN_PORT)
787 .append(srcInstancePort.portNumber().toString())
788 .append(COMMA)
789 .append(NW_SRC)
790 .append(srcIp)
791 .append(COMMA);
792
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900793 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900794 Type netType = osNetService.networkType(srcInstancePort.networkId());
795 if (netType == Type.VXLAN || netType == Type.GRE ||
796 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900797 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900798 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900799 requestStringBuilder.append(DL_DST)
800 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900801 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
802 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900803 requestStringBuilder.append(DL_DST)
804 .append(DEFAULT_GATEWAY_MAC_STR)
805 .append(COMMA);
806 }
807 } else {
808 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900809 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900810 }
811 }
812
813 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900814 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900815 .append("\n");
816 } else {
817 requestStringBuilder.append(COMMA)
818 .append(NW_SRC)
819 .append(dstIp)
820 .append(COMMA);
821
Jian Li621f73c2018-12-15 01:49:22 +0900822 Type netType = osNetService.networkType(srcInstancePort.networkId());
823
824 if (netType == Type.VXLAN || netType == Type.GRE ||
825 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900826 requestStringBuilder.append(TUN_ID)
827 .append(osNetService.segmentId(srcInstancePort.networkId()))
828 .append(COMMA);
829 }
830 requestStringBuilder.append(NW_DST)
831 .append(srcIp)
832 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900833 }
834
835 return requestStringBuilder.toString();
836 }
837
838 /**
839 * Sends flow trace string to node.
840 *
841 * @param requestString reqeust string
842 * @param node src node
843 * @return flow trace result in string format
844 */
845 public static String sendTraceRequestToNode(String requestString,
846 OpenstackNode node) {
847 String traceResult = null;
848 OpenstackSshAuth sshAuth = node.sshAuthInfo();
849
850 try (SshClient client = SshClient.setUpDefaultClient()) {
851 client.start();
852
853 try (ClientSession session = client
854 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
855 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
856 session.addPasswordIdentity(sshAuth.password());
857 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
858
859
860 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
861
862 log.debug("requestString: {}", requestString);
863 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900864 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900865
Jian Li5ecfd1a2018-12-10 11:41:03 +0900866 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +0900867 OutputStream errStream = new ByteArrayOutputStream();
868
869 channel.setIn(new NoCloseInputStream(inputStream));
870 channel.setErr(errStream);
871 channel.setOut(outputStream);
872
873 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
874 eventList.add(ClientChannelEvent.OPENED);
875
876 OpenFuture channelFuture = channel.open();
877
878 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
879
880 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
881
882 while (!channelFuture.isOpened()) {
883 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
884 log.error("Failed to open channel");
885 return null;
886 }
887 }
888 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
889
Jian Li5ecfd1a2018-12-10 11:41:03 +0900890 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +0900891
892 channel.close();
893 }
894 } finally {
895 session.close();
896 }
897 } finally {
898 client.stop();
899 }
900
901 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900902 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +0900903 }
904
905 return traceResult;
906 }
907
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900908 /**
909 * Returns the floating ip with supplied instance port.
910 *
911 * @param instancePort instance port
912 * @param osRouterAdminService openstack router admin service
913 * @return floating ip
914 */
915 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900916 OpenstackRouterAdminService
917 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900918 return osRouterAdminService.floatingIps().stream()
919 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
920 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
921 .findAny().orElse(null);
922 }
923
924 /**
925 * Sends GARP packet with supplied floating ip information.
926 *
927 * @param floatingIP floating ip
928 * @param instancePort instance port
929 * @param vlanId vlain id
930 * @param gatewayNode gateway node
931 * @param packetService packet service
932 */
Jian Li32b03622018-11-06 17:54:24 +0900933 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
934 InstancePort instancePort,
935 VlanId vlanId,
936 OpenstackNode gatewayNode,
937 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900938 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
939
940 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
941 .setOutput(gatewayNode.uplinkPortNum()).build();
942
943 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
944 ByteBuffer.wrap(ethernet.serialize())));
945 }
946
947 /**
948 * Returns the external peer router with supplied network information.
949 *
950 * @param network network
951 * @param osNetworkService openstack network service
952 * @param osRouterAdminService openstack router admin service
953 * @return external peer router
954 */
955 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900956 OpenstackNetworkService
957 osNetworkService,
958 OpenstackRouterAdminService
959 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900960 if (network == null) {
961 return null;
962 }
963
Jian Lie6e609f2019-05-14 17:45:54 +0900964 Subnet subnet = osNetworkService.subnets(network.getId())
965 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900966
967 if (subnet == null) {
968 return null;
969 }
970
971 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
972 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
973 .findAny().orElse(null);
974 if (osRouterIface == null) {
975 return null;
976 }
977
978 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900979 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900980 return null;
981 }
982
983 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
984 return osNetworkService.externalPeerRouter(exGatewayInfo);
985
986 }
987
Jian Liebde74d2018-11-14 00:18:57 +0900988 /**
989 * Returns the external peer router with specified subnet information.
990 *
991 * @param subnet openstack subnet
992 * @param osRouterService openstack router service
993 * @param osNetworkService openstack network service
994 * @return external peer router
995 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900996 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
997 OpenstackRouterService
998 osRouterService,
999 OpenstackNetworkService
1000 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001001 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
1002 if (osRouter == null) {
1003 return null;
1004 }
1005 if (osRouter.getExternalGatewayInfo() == null) {
1006 // this router does not have external connectivity
1007 log.trace("router({}) has no external gateway",
1008 osRouter.getName());
1009 return null;
1010 }
1011
1012 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1013 }
1014
1015 /**
1016 * Returns the external ip address with specified router information.
1017 *
1018 * @param srcSubnet source subnet
1019 * @param osRouterService openstack router service
1020 * @param osNetworkService openstack network service
1021 * @return external ip address
1022 */
1023 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001024 OpenstackRouterService
1025 osRouterService,
1026 OpenstackNetworkService
1027 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001028
1029 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1030
1031 if (osRouter.getExternalGatewayInfo() == null) {
1032 // this router does not have external connectivity
1033 log.trace("router({}) has no external gateway",
1034 osRouter.getName());
1035 return null;
1036 }
1037
1038 return getExternalIp(osRouter, osNetworkService);
1039 }
1040
1041 /**
1042 * Returns the external ip address with specified router information.
1043 *
1044 * @param router openstack router
1045 * @param osNetworkService openstack network service
1046 * @return external ip address
1047 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001048 public static IpAddress getExternalIp(Router router,
1049 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001050 if (router == null) {
1051 return null;
1052 }
1053
1054 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1055 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001056 log.trace("Failed to get externalIp for router {} because " +
1057 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001058 router.getId());
1059 return null;
1060 }
1061
1062 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1063 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1064 .stream()
1065 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1066 .findAny().orElse(null);
1067
1068 if (exGatewayPort == null) {
1069 return null;
1070 }
1071
1072 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1073 .findAny().get().getIpAddress());
1074 }
1075
Jian Li2d68c192018-12-13 15:52:59 +09001076 /**
1077 * Returns the tunnel port number with specified net ID and openstack node.
1078 *
1079 * @param netId network ID
1080 * @param netService network service
1081 * @param osNode openstack node
1082 * @return tunnel port number
1083 */
1084 public static PortNumber tunnelPortNumByNetId(String netId,
1085 OpenstackNetworkService netService,
1086 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001087 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001088
1089 if (netType == null) {
1090 return null;
1091 }
1092
1093 return tunnelPortNumByNetType(netType, osNode);
1094 }
1095
1096 /**
1097 * Returns the tunnel port number with specified net type and openstack node.
1098 *
1099 * @param netType network type
1100 * @param osNode openstack node
1101 * @return tunnel port number
1102 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001103 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001104 switch (netType) {
1105 case VXLAN:
1106 return osNode.vxlanTunnelPortNum();
1107 case GRE:
1108 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001109 case GENEVE:
1110 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001111 default:
1112 return null;
1113 }
1114 }
1115
Jian Li7b8c3682019-05-12 13:57:15 +09001116 /**
1117 * Returns the REST URL of active node.
1118 *
1119 * @param haService openstack HA service
1120 * @return REST URL of active node
1121 */
1122 public static String getActiveUrl(OpenstackHaService haService) {
1123 return "http://" + haService.getActiveIp().toString() + ":" +
1124 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1125 }
1126
1127 /**
1128 * Returns the REST client instance with given resource path.
1129 *
1130 * @param haService openstack HA service
1131 * @param resourcePath resource path
1132 * @return REST client instance
1133 */
1134 public static WebTarget getActiveClient(OpenstackHaService haService,
1135 String resourcePath) {
1136 HttpAuthenticationFeature feature =
1137 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1138 Client client = ClientBuilder.newClient().register(feature);
1139 return client.target(getActiveUrl(haService)).path(resourcePath);
1140 }
1141
1142 /**
1143 * Returns the post response from the active node.
1144 *
1145 * @param haService openstack HA service
1146 * @param resourcePath resource path
1147 * @param input input
1148 * @return post response
1149 */
1150 public static Response syncPost(OpenstackHaService haService,
1151 String resourcePath,
1152 String input) {
1153
1154 log.debug("Sync POST request with {} on {}",
1155 haService.getActiveIp().toString(), resourcePath);
1156
1157 return getActiveClient(haService, resourcePath)
1158 .request(APPLICATION_JSON_TYPE)
1159 .post(Entity.json(input));
1160 }
1161
1162 /**
1163 * Returns the put response from the active node.
1164 *
1165 * @param haService openstack HA service
1166 * @param resourcePath resource path
1167 * @param id resource identifier
1168 * @param input input
1169 * @return put response
1170 */
1171 public static Response syncPut(OpenstackHaService haService,
1172 String resourcePath,
1173 String id, String input) {
1174 return syncPut(haService, resourcePath, null, id, input);
1175 }
1176
1177 /**
1178 * Returns the put response from the active node.
1179 *
1180 * @param haService openstack HA service
1181 * @param resourcePath resource path
1182 * @param id resource identifier
1183 * @param suffix resource suffix
1184 * @param input input
1185 * @return put response
1186 */
1187 public static Response syncPut(OpenstackHaService haService,
1188 String resourcePath,
1189 String suffix,
1190 String id, String input) {
1191
1192 log.debug("Sync PUT request with {} on {}",
1193 haService.getActiveIp().toString(), resourcePath);
1194
1195 String pathStr = "/" + id;
1196
1197 if (suffix != null) {
1198 pathStr += "/" + suffix;
1199 }
1200
1201 return getActiveClient(haService, resourcePath)
1202 .path(pathStr)
1203 .request(APPLICATION_JSON_TYPE)
1204 .put(Entity.json(input));
1205 }
1206
1207 /**
1208 * Returns the delete response from the active node.
1209 *
1210 * @param haService openstack HA service
1211 * @param resourcePath resource path
1212 * @param id resource identifier
1213 * @return delete response
1214 */
1215 public static Response syncDelete(OpenstackHaService haService,
1216 String resourcePath,
1217 String id) {
1218
1219 log.debug("Sync DELETE request with {} on {}",
1220 haService.getActiveIp().toString(), resourcePath);
1221
1222 return getActiveClient(haService, resourcePath)
1223 .path("/" + id)
1224 .request(APPLICATION_JSON_TYPE)
1225 .delete();
1226 }
1227
Jian Li51728702019-05-17 18:38:56 +09001228 /**
1229 * Gets the ovsdb client with supplied openstack node.
1230 *
1231 * @param node openstack node
1232 * @param ovsdbPort openvswitch DB port number
1233 * @param controller openvswitch DB controller instance
1234 * @return ovsdb client instance
1235 */
1236 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1237 OvsdbController controller) {
1238 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1239 return controller.getOvsdbClient(ovsdb);
1240 }
1241
1242 /**
1243 * Obtains the name of interface attached to the openstack VM.
1244 *
1245 * @param portId openstack port identifier
1246 * @return name of interface
1247 */
1248 public static String ifaceNameFromOsPortId(String portId) {
1249 if (portId != null) {
1250 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1251 }
1252
1253 return null;
1254 }
1255
Jian Li5ecfd1a2018-12-10 11:41:03 +09001256 private static Router getRouterFromSubnet(Subnet subnet,
1257 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001258 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1259 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1260 .findAny().orElse(null);
1261 if (osRouterIface == null) {
1262 return null;
1263 }
1264
1265 return osRouterService.router(osRouterIface.getId());
1266 }
1267
Daniel Park7e8c4d82018-08-13 23:47:49 +09001268 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001269 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001270 }
1271
Daniel Park2ff66b42018-08-01 11:52:45 +09001272 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001273 * Returns GARP packet with supplied floating ip and instance port information.
1274 *
1275 * @param floatingIP floating ip
1276 * @param instancePort instance port
1277 * @param vlanId vlan id
1278 * @return GARP packet
1279 */
1280 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1281 InstancePort instancePort,
1282 VlanId vlanId) {
1283 Ethernet ethernet = new Ethernet();
1284 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1285 ethernet.setSourceMACAddress(instancePort.macAddress());
1286 ethernet.setEtherType(Ethernet.TYPE_ARP);
1287 ethernet.setVlanID(vlanId.id());
1288
1289 ARP arp = new ARP();
1290 arp.setOpCode(ARP.OP_REPLY);
1291 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1292 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1293
1294 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1295 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1296
1297 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1298 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1299
Jian Li5ecfd1a2018-12-10 11:41:03 +09001300 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1301 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001302
1303 ethernet.setPayload(arp);
1304
1305 return ethernet;
1306 }
1307
1308 /**
Jian Lia271b3c2019-09-03 23:10:20 +09001309 * Re-structures the OVS port name.
1310 * The length of OVS port name should be not large than 15.
1311 *
1312 * @param portName original port name
1313 * @return re-structured OVS port name
1314 */
1315 public static String structurePortName(String portName) {
1316
1317 // The size of OVS port name should not be larger than 15
1318 if (portName.length() > PORT_NAME_MAX_LENGTH) {
1319 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
1320 }
1321
1322 return portName;
1323 }
1324
1325 /**
Jian Lid5727622019-09-11 11:15:16 +09001326 * Obtains flow group key from the given id.
1327 *
1328 * @param groupId flow group identifier
1329 * @return flow group key
1330 */
1331 public static GroupKey getGroupKey(int groupId) {
1332 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
1333 }
1334
1335 /**
Jian Li51b844c2018-05-31 10:59:03 +09001336 * Builds up and a complete endpoint URL from gateway node.
1337 *
1338 * @param node gateway node
1339 * @return a complete endpoint URL
1340 */
1341 private static String buildEndpoint(OpenstackNode node) {
1342
Jian Lic704b672018-09-04 18:52:53 +09001343 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001344
1345 StringBuilder endpointSb = new StringBuilder();
1346 endpointSb.append(auth.protocol().name().toLowerCase());
1347 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001348 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001349 return endpointSb.toString();
1350 }
1351
1352 /**
1353 * Obtains the SSL config without verifying the certification.
1354 *
1355 * @return SSL config
1356 */
1357 private static Config getSslConfig() {
1358 // we bypass the SSL certification verification for now
1359 // TODO: verify server side SSL using a given certification
1360 Config config = Config.newConfig().withSSLVerificationDisabled();
1361
1362 TrustManager[] trustAllCerts = new TrustManager[]{
1363 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001364 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001365 public X509Certificate[] getAcceptedIssuers() {
1366 return null;
1367 }
1368
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001369 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001370 public void checkClientTrusted(X509Certificate[] certs,
1371 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001372 return;
Jian Li51b844c2018-05-31 10:59:03 +09001373 }
1374
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001375 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001376 public void checkServerTrusted(X509Certificate[] certs,
1377 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001378 return;
Jian Li51b844c2018-05-31 10:59:03 +09001379 }
1380 }
1381 };
1382
1383 HostnameVerifier allHostsValid = (hostname, session) -> true;
1384
1385 try {
1386 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1387 sc.init(null, trustAllCerts,
1388 new java.security.SecureRandom());
1389 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1390 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1391
1392 config.withSSLContext(sc);
1393 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001394 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001395 return null;
1396 }
1397
1398 return config;
1399 }
1400
1401 /**
1402 * Obtains the facing object with given openstack perspective.
1403 *
1404 * @param perspective keystone perspective
1405 * @return facing object
1406 */
1407 private static Facing getFacing(Perspective perspective) {
1408
1409 switch (perspective) {
1410 case PUBLIC:
1411 return Facing.PUBLIC;
1412 case ADMIN:
1413 return Facing.ADMIN;
1414 case INTERNAL:
1415 return Facing.INTERNAL;
1416 default:
1417 return null;
1418 }
1419 }
1420
1421 /**
1422 * Obtains gateway instance by giving index number.
1423 *
1424 * @param gws a collection of gateway nodes
1425 * @param index index number
1426 * @return gateway instance
1427 */
1428 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1429 Map<String, OpenstackNode> hashMap = new HashMap<>();
1430 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1431 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1432 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1433
1434 int intIndex = 0;
1435 OpenstackNode gw = null;
1436 while (iteratorKey.hasNext()) {
1437 String key = iteratorKey.next();
1438
1439 if (intIndex == index) {
1440 gw = treeMap.get(key);
1441 }
1442 intIndex++;
1443 }
1444 return gw;
1445 }
Jian Li63430202018-08-30 16:24:09 +09001446}