blob: 615de0e7440d0f9b411daf801e1f17629d620128 [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 Li40f032a2019-10-02 20:36:09 +090088import org.openstack4j.model.network.IP;
Jian Li24ec59f2018-05-23 19:01:25 +090089import org.openstack4j.model.network.NetFloatingIP;
90import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090091import org.openstack4j.model.network.Port;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090092import org.openstack4j.model.network.Router;
Jian Li0b564282018-06-20 00:50:53 +090093import org.openstack4j.model.network.RouterInterface;
Jian Li40f032a2019-10-02 20:36:09 +090094import org.openstack4j.model.network.SecurityGroup;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090095import org.openstack4j.model.network.Subnet;
Jian Li51b844c2018-05-31 10:59:03 +090096import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090097import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090098import org.slf4j.Logger;
99import org.slf4j.LoggerFactory;
100
Jian Li63430202018-08-30 16:24:09 +0900101import javax.crypto.Mac;
102import javax.crypto.spec.SecretKeySpec;
Jian Li51b844c2018-05-31 10:59:03 +0900103import javax.net.ssl.HostnameVerifier;
104import javax.net.ssl.HttpsURLConnection;
105import javax.net.ssl.SSLContext;
106import javax.net.ssl.TrustManager;
107import javax.net.ssl.X509TrustManager;
Jian Li7b8c3682019-05-12 13:57:15 +0900108import javax.ws.rs.client.Client;
109import javax.ws.rs.client.ClientBuilder;
110import javax.ws.rs.client.Entity;
111import javax.ws.rs.client.WebTarget;
112import javax.ws.rs.core.Response;
Jian Li63430202018-08-30 16:24:09 +0900113import java.io.ByteArrayInputStream;
114import java.io.ByteArrayOutputStream;
Jian Li0b564282018-06-20 00:50:53 +0900115import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +0900116import java.io.InputStream;
Daniel Parka73c2362018-09-17 17:43:25 +0900117import java.io.OutputStream;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900118import java.nio.ByteBuffer;
Jian Li51b844c2018-05-31 10:59:03 +0900119import java.security.cert.X509Certificate;
Daniel Parka73c2362018-09-17 17:43:25 +0900120import java.util.Collection;
Jian Li1064e4f2018-05-29 16:16:53 +0900121import java.util.HashMap;
122import java.util.Iterator;
Jian Li40f032a2019-10-02 20:36:09 +0900123import java.util.List;
Jian Li1064e4f2018-05-29 16:16:53 +0900124import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +0900125import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +0900126import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +0900127import java.util.Set;
128import java.util.TreeMap;
Daniel Parka73c2362018-09-17 17:43:25 +0900129import java.util.concurrent.TimeUnit;
Jian Li40f032a2019-10-02 20:36:09 +0900130import java.util.stream.Collectors;
Jian Li091d8d22018-02-20 10:42:06 +0900131
132import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +0900133import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +0900134import static com.google.common.base.Strings.isNullOrEmpty;
Jian Li7b8c3682019-05-12 13:57:15 +0900135import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
136import static org.apache.commons.io.IOUtils.toInputStream;
Jian Li5ecfd1a2018-12-10 11:41:03 +0900137import static org.onlab.packet.Ip4Address.valueOf;
Daniel Park95f73312018-07-31 15:48:34 +0900138import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka73c2362018-09-17 17:43:25 +0900139import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Li40f032a2019-10-02 20:36:09 +0900140import static org.onosproject.openstacknetworking.api.Constants.FLOATING_IP_FORMAT;
141import static org.onosproject.openstacknetworking.api.Constants.NETWORK_FORMAT;
Jian Li7b8c3682019-05-12 13:57:15 +0900142import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_REST_PATH;
Daniel Parkc4d06402018-05-28 15:57:37 +0900143import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
144import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Jian Li40f032a2019-10-02 20:36:09 +0900145import static org.onosproject.openstacknetworking.api.Constants.PORT_FORMAT;
Daniel Park7e8c4d82018-08-13 23:47:49 +0900146import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
147import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
Jian Li7b8c3682019-05-12 13:57:15 +0900148import static org.onosproject.openstacknetworking.api.Constants.REST_PASSWORD;
149import static org.onosproject.openstacknetworking.api.Constants.REST_PORT;
150import static org.onosproject.openstacknetworking.api.Constants.REST_USER;
151import static org.onosproject.openstacknetworking.api.Constants.REST_UTF8;
Jian Li40f032a2019-10-02 20:36:09 +0900152import static org.onosproject.openstacknetworking.api.Constants.ROUTER_FORMAT;
153import static org.onosproject.openstacknetworking.api.Constants.ROUTER_INTF_FORMAT;
154import static org.onosproject.openstacknetworking.api.Constants.SECURITY_GROUP_FORMAT;
155import static org.onosproject.openstacknetworking.api.Constants.SUBNET_FORMAT;
Daniel Parkec9d1132018-08-19 11:18:03 +0900156import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Ray Milkey9dc57392018-06-08 08:52:31 -0700157import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +0900158import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +0900159
160/**
161 * An utility that used in openstack networking app.
162 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900163public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +0900164
Daniel Park95985382018-07-23 11:38:07 +0900165 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +0900166
Daniel Parkc4d06402018-05-28 15:57:37 +0900167 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +0900168 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +0900169 private static final String PREFIX_DEVICE_NUMBER = "s";
170 private static final String PREFIX_FUNCTION_NUMBER = "f";
171
Jian Li51b844c2018-05-31 10:59:03 +0900172 // keystone endpoint related variables
173 private static final String DOMAIN_DEFAULT = "default";
174 private static final String KEYSTONE_V2 = "v2.0";
175 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +0900176 private static final String SSL_TYPE = "SSL";
177
Jian Li7f024de2018-07-07 03:51:02 +0900178 private static final String PROXY_MODE = "proxy";
179 private static final String BROADCAST_MODE = "broadcast";
180
Jian Licad36c72018-09-13 17:44:54 +0900181 private static final String ENABLE = "enable";
182 private static final String DISABLE = "disable";
183
Jian Li63430202018-08-30 16:24:09 +0900184 private static final int HTTP_PAYLOAD_BUFFER = 8 * 1024;
185
186 private static final String HMAC_SHA256 = "HmacSHA256";
187
Jian Li24ec59f2018-05-23 19:01:25 +0900188 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
189
Daniel Parka73c2362018-09-17 17:43:25 +0900190 private static final String DL_DST = "dl_dst=";
191 private static final String NW_DST = "nw_dst=";
192 private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
193 private static final String IN_PORT = "in_port=";
194 private static final String NW_SRC = "nw_src=";
195 private static final String COMMA = ",";
196 private static final String TUN_ID = "tun_id=";
197
Jian Li40f032a2019-10-02 20:36:09 +0900198 private static final String DEVICE_OWNER_GW = "network:router_gateway";
199 private static final String DEVICE_OWNER_IFACE = "network:router_interface";
200
201 private static final String NOT_AVAILABLE = "N/A";
202
Daniel Parka73c2362018-09-17 17:43:25 +0900203 private static final long TIMEOUT_MS = 5000;
204 private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
205 private static final int SSH_PORT = 22;
206
Jian Li51728702019-05-17 18:38:56 +0900207 private static final int TAP_PORT_LENGTH = 11;
Jian Lia271b3c2019-09-03 23:10:20 +0900208 private static final int PORT_NAME_MAX_LENGTH = 15;
Jian Li51728702019-05-17 18:38:56 +0900209
Jian Li091d8d22018-02-20 10:42:06 +0900210 /**
211 * Prevents object instantiation from external.
212 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900213 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900214 }
215
216 /**
217 * Interprets JSON string to corresponding openstack model entity object.
218 *
Jian Li7b8c3682019-05-12 13:57:15 +0900219 * @param inputStr JSON string
Jian Li091d8d22018-02-20 10:42:06 +0900220 * @param entityClazz openstack model entity class
221 * @return openstack model entity object
222 */
Jian Li7b8c3682019-05-12 13:57:15 +0900223 public static ModelEntity jsonToModelEntity(String inputStr, Class entityClazz) {
Jian Li091d8d22018-02-20 10:42:06 +0900224 ObjectMapper mapper = new ObjectMapper();
225 try {
Jian Li7b8c3682019-05-12 13:57:15 +0900226 InputStream input = toInputStream(inputStr, REST_UTF8);
Jian Li091d8d22018-02-20 10:42:06 +0900227 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
228 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
229 return ObjectMapperSingleton.getContext(entityClazz)
230 .readerFor(entityClazz)
231 .readValue(jsonTree);
232 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900233 log.error("Exception occurred because of {}", e);
Jian Li091d8d22018-02-20 10:42:06 +0900234 throw new IllegalArgumentException();
235 }
236 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900237
238 /**
239 * Converts openstack model entity object into JSON object.
240 *
241 * @param entity openstack model entity object
242 * @param entityClazz openstack model entity class
243 * @return JSON object
244 */
245 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
246 ObjectMapper mapper = new ObjectMapper();
247 try {
248 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
249 .writerFor(entityClazz)
250 .writeValueAsString(entity);
251 log.trace(strModelEntity);
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900252 return (ObjectNode) mapper.readTree(strModelEntity.getBytes(Charsets.UTF_8));
Daniel Park95985382018-07-23 11:38:07 +0900253 } catch (IOException e) {
254 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900255 throw new IllegalStateException();
256 }
257 }
Jian Li1064e4f2018-05-29 16:16:53 +0900258
259 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900260 * Obtains a floating IP associated with the given instance port.
261 *
262 * @param port instance port
263 * @param fips a collection of floating IPs
264 * @return associated floating IP
265 */
266 public static NetFloatingIP associatedFloatingIp(InstancePort port,
267 Set<NetFloatingIP> fips) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900268 for (NetFloatingIP fip : fips) {
269 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
270 continue;
Jian Li24ec59f2018-05-23 19:01:25 +0900271 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900272 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
273 continue;
274 }
Jian Li6bc29d92018-10-02 13:55:05 +0900275 if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
276 fip.getPortId().equals(port.portId())) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900277 return fip;
278 }
Jian Li24ec59f2018-05-23 19:01:25 +0900279 }
Daniel Park2ff66b42018-08-01 11:52:45 +0900280
Jian Li24ec59f2018-05-23 19:01:25 +0900281 return null;
282 }
283
284 /**
285 * Checks whether the given floating IP is associated with a VM.
286 *
287 * @param service openstack network service
288 * @param fip floating IP
289 * @return true if the given floating IP associated with a VM, false otherwise
290 */
291 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
292 NetFloatingIP fip) {
293 Port osPort = service.port(fip.getPortId());
294 if (osPort == null) {
295 return false;
296 }
297
298 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
299 Network osNet = service.network(osPort.getNetworkId());
300 if (osNet == null) {
301 final String errorFormat = ERR_FLOW + "no network(%s) exists";
302 final String error = String.format(errorFormat,
303 fip.getFloatingIpAddress(), osPort.getNetworkId());
304 throw new IllegalStateException(error);
305 }
306 return true;
307 } else {
308 return false;
309 }
310 }
311
312 /**
Jian Lia171a432018-06-11 11:52:11 +0900313 * Obtains the gateway node by instance port.
314 *
315 * @param gateways a collection of gateway nodes
316 * @param instPort instance port
317 * @return a gateway node
318 */
319 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
320 InstancePort instPort) {
321 OpenstackNode gw = null;
322 if (instPort != null && instPort.deviceId() != null) {
323 gw = getGwByComputeDevId(gateways, instPort.deviceId());
324 }
325 return gw;
326 }
327
328 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900329 * Obtains the gateway node by device in compute node. Note that the gateway
330 * node is determined by device's device identifier.
331 *
332 * @param gws a collection of gateway nodes
333 * @param deviceId device identifier
334 * @return a gateway node
335 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900336 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws,
337 DeviceId deviceId) {
Jian Li1064e4f2018-05-29 16:16:53 +0900338 int numOfGw = gws.size();
339
340 if (numOfGw == 0) {
341 return null;
342 }
343
344 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
345
346 return getGwByIndex(gws, gwIndex);
347 }
348
Jian Li51b844c2018-05-31 10:59:03 +0900349 /**
350 * Obtains a connected openstack client.
351 *
352 * @param osNode openstack node
353 * @return a connected openstack client
354 */
355 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900356 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900357 String endpoint = buildEndpoint(osNode);
358 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900359
Jian Li51b844c2018-05-31 10:59:03 +0900360 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900361
Jian Li51b844c2018-05-31 10:59:03 +0900362 try {
363 if (endpoint.contains(KEYSTONE_V2)) {
364 IOSClientBuilder.V2 builder = OSFactory.builderV2()
365 .endpoint(endpoint)
366 .tenantName(auth.project())
367 .credentials(auth.username(), auth.password())
368 .withConfig(config);
369
370 if (perspective != null) {
371 builder.perspective(getFacing(perspective));
372 }
373
374 return builder.authenticate();
375 } else if (endpoint.contains(KEYSTONE_V3)) {
376
377 Identifier project = Identifier.byName(auth.project());
378 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
379
380 IOSClientBuilder.V3 builder = OSFactory.builderV3()
381 .endpoint(endpoint)
382 .credentials(auth.username(), auth.password(), domain)
383 .scopeToProject(project, domain)
384 .withConfig(config);
385
386 if (perspective != null) {
387 builder.perspective(getFacing(perspective));
388 }
389
390 return builder.authenticate();
391 } else {
392 log.warn("Unrecognized keystone version type");
393 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900394 }
Jian Li51b844c2018-05-31 10:59:03 +0900395 } catch (AuthenticationException e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900396 log.error("Authentication failed due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +0900397 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900398 }
Jian Li1064e4f2018-05-29 16:16:53 +0900399 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900400
401 /**
402 * Extract the interface name with the supplied port.
403 *
404 * @param port port
405 * @return interface name
406 */
407 public static String getIntfNameFromPciAddress(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900408 String intfName;
409
Daniel Park95985382018-07-23 11:38:07 +0900410 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900411 log.error("Port profile is not found");
412 return null;
413 }
414
Daniel Park95985382018-07-23 11:38:07 +0900415 if (!port.getProfile().containsKey(PCISLOT) ||
416 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900417 log.error("Failed to retrieve the interface name because of no " +
418 "pci_slot information from the port");
Daniel Parkc4d06402018-05-28 15:57:37 +0900419 return null;
420 }
Jian Li51b844c2018-05-31 10:59:03 +0900421
Daniel Parkff178ba2018-11-23 15:57:24 +0900422 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
423
424 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
425 log.debug("{} is an non-smart NIC prefix.", vendorInfoForPort);
426 return UNSUPPORTED_VENDOR;
427 }
428
429 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
430
Daniel Parkc4d06402018-05-28 15:57:37 +0900431 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
432 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
433
434 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
435 .split(":")[2]
436 .split("\\.")[0];
437 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
438
439 String functionNumHex = port.getProfile().get(PCISLOT).toString()
440 .split(":")[2]
441 .split("\\.")[1];
442 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
443
Daniel Parkc4d06402018-05-28 15:57:37 +0900444 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
445 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
446 } else {
447 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
448 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
449 }
450
451 return intfName;
452 }
Jian Li51b844c2018-05-31 10:59:03 +0900453
454 /**
Daniel Park95f73312018-07-31 15:48:34 +0900455 * Check if the given interface is added to the given device or not.
456 *
457 * @param deviceId device ID
458 * @param intfName interface name
459 * @param deviceService device service
460 * @return true if the given interface is added to the given device or false otherwise
461 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900462 public static boolean hasIntfAleadyInDevice(DeviceId deviceId,
463 String intfName,
464 DeviceService deviceService) {
Daniel Park95f73312018-07-31 15:48:34 +0900465 checkNotNull(deviceId);
466 checkNotNull(intfName);
467
Jian Li5ecfd1a2018-12-10 11:41:03 +0900468 return deviceService.getPorts(deviceId).stream().anyMatch(port ->
469 Objects.equals(port.annotations().value(PORT_NAME), intfName));
Daniel Park95f73312018-07-31 15:48:34 +0900470 }
471
472 /**
Jian Li0b564282018-06-20 00:50:53 +0900473 * Adds router interfaces to openstack admin service.
Jian Li0b564282018-06-20 00:50:53 +0900474 *
475 * @param osPort port
476 * @param adminService openstack admin service
477 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900478 public static void addRouterIface(Port osPort,
479 OpenstackRouterAdminService adminService) {
Jian Li0b564282018-06-20 00:50:53 +0900480 osPort.getFixedIps().forEach(p -> {
481 JsonNode jsonTree = new ObjectMapper().createObjectNode()
482 .put("id", osPort.getDeviceId())
483 .put("tenant_id", osPort.getTenantId())
484 .put("subnet_id", p.getSubnetId())
485 .put("port_id", osPort.getId());
486 try {
487 RouterInterface rIface = getContext(NeutronRouterInterface.class)
488 .readerFor(NeutronRouterInterface.class)
489 .readValue(jsonTree);
490 if (adminService.routerInterface(rIface.getPortId()) != null) {
491 adminService.updateRouterInterface(rIface);
492 } else {
493 adminService.addRouterInterface(rIface);
494 }
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900495 } catch (IOException e) {
496 log.error("IOException occurred because of {}", e);
Jian Li0b564282018-06-20 00:50:53 +0900497 }
498 });
499 }
500
501 /**
Jian Li40f032a2019-10-02 20:36:09 +0900502 * Prints openstack security group.
503 *
504 * @param osSg openstack security group
505 */
506 public static void printSecurityGroup(SecurityGroup osSg) {
507 print(SECURITY_GROUP_FORMAT, osSg.getId(), osSg.getName());
508 }
509
510 /**
511 * Prints openstack network.
512 *
513 * @param osNet openstack network
514 */
515 public static void printNetwork(Network osNet) {
516 final String strNet = String.format(NETWORK_FORMAT,
517 osNet.getId(),
518 osNet.getName(),
519 osNet.getProviderSegID(),
520 osNet.getSubnets());
521 print(strNet);
522 }
523
524 /**
525 * Prints openstack subnet.
526 *
527 * @param osSubnet openstack subnet
528 * @param osNetService openstack network service
529 */
530 public static void printSubnet(Subnet osSubnet,
531 OpenstackNetworkService osNetService) {
532 final Network network = osNetService.network(osSubnet.getNetworkId());
533 final String netName = network == null ? NOT_AVAILABLE : network.getName();
534 final String strSubnet = String.format(SUBNET_FORMAT,
535 osSubnet.getId(),
536 netName,
537 osSubnet.getCidr());
538 print(strSubnet);
539 }
540
541 /**
542 * Prints openstack port.
543 *
544 * @param osPort openstack port
545 * @param osNetService openstack network service
546 */
547 public static void printPort(Port osPort,
548 OpenstackNetworkService osNetService) {
549 List<String> fixedIps = osPort.getFixedIps().stream()
550 .map(IP::getIpAddress)
551 .collect(Collectors.toList());
552 final Network network = osNetService.network(osPort.getNetworkId());
553 final String netName = network == null ? NOT_AVAILABLE : network.getName();
554 final String strPort = String.format(PORT_FORMAT,
555 osPort.getId(),
556 netName,
557 osPort.getMacAddress(),
558 fixedIps.isEmpty() ? "" : fixedIps);
559 print(strPort);
560 }
561
562 /**
563 * Prints openstack router.
564 *
565 * @param osRouter openstack router
566 * @param osNetService openstack network service
567 */
568 public static void printRouter(Router osRouter,
569 OpenstackNetworkService osNetService) {
570 List<String> externals = osNetService.ports().stream()
571 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
572 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_GW))
573 .flatMap(osPort -> osPort.getFixedIps().stream())
574 .map(IP::getIpAddress)
575 .collect(Collectors.toList());
576
577 List<String> internals = osNetService.ports().stream()
578 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
579 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
580 .flatMap(osPort -> osPort.getFixedIps().stream())
581 .map(IP::getIpAddress)
582 .collect(Collectors.toList());
583
584 final String strRouter = String.format(ROUTER_FORMAT,
585 osRouter.getId(),
586 osRouter.getName(),
587 externals.isEmpty() ? "" : externals,
588 internals.isEmpty() ? "" : internals);
589 print(strRouter);
590 }
591
592 /**
593 * Prints openstack router interface.
594 *
595 * @param osRouterIntf openstack router interface
596 */
597 public static void printRouterIntf(RouterInterface osRouterIntf) {
598 final String strRouterIntf = String.format(ROUTER_INTF_FORMAT,
599 osRouterIntf.getId(),
600 osRouterIntf.getTenantId(),
601 osRouterIntf.getSubnetId());
602 print(strRouterIntf);
603 }
604
605 /**
606 * Prints openstack floating IP.
607 *
608 * @param floatingIp floating IP
609 */
610 public static void printFloatingIp(NetFloatingIP floatingIp) {
611 final String strFloating = String.format(FLOATING_IP_FORMAT,
612 floatingIp.getId(),
613 floatingIp.getFloatingIpAddress(),
614 Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
615 "" : floatingIp.getFixedIpAddress());
616 print(strFloating);
617 }
618
619 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900620 * Obtains the property value with specified property key name.
621 *
622 * @param properties a collection of properties
623 * @param name key name
624 * @return mapping value
625 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900626 public static String getPropertyValue(Set<ConfigProperty> properties,
627 String name) {
Jian Li7f70bb72018-07-06 23:35:30 +0900628 Optional<ConfigProperty> property =
629 properties.stream().filter(p -> p.name().equals(name)).findFirst();
630 return property.map(ConfigProperty::value).orElse(null);
631 }
632
633 /**
Jian Li9d35bd62018-10-13 01:43:24 +0900634 * Obtains the boolean property value with specified property key name.
635 *
636 * @param properties a collection of properties
637 * @param name key name
638 * @return mapping value
639 */
Jian Li5ecfd1a2018-12-10 11:41:03 +0900640 public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
641 String name) {
Jian Li9d35bd62018-10-13 01:43:24 +0900642 Optional<ConfigProperty> property =
643 properties.stream().filter(p -> p.name().equals(name)).findFirst();
644
645 return property.map(ConfigProperty::asBoolean).orElse(false);
646 }
647
648 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900649 * Prints out the JSON string in pretty format.
650 *
651 * @param mapper Object mapper
652 * @param jsonString JSON string
653 * @return pretty formatted JSON string
654 */
655 public static String prettyJson(ObjectMapper mapper, String jsonString) {
656 try {
657 Object jsonObject = mapper.readValue(jsonString, Object.class);
658 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
Daniel Park2ff66b42018-08-01 11:52:45 +0900659 } catch (JsonParseException e) {
660 log.debug("JsonParseException caused by {}", e);
661 } catch (JsonMappingException e) {
662 log.debug("JsonMappingException caused by {}", e);
663 } catch (JsonProcessingException e) {
664 log.debug("JsonProcessingException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900665 } catch (IOException e) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900666 log.debug("IOException caused by {}", e);
Jian Lif1efbe52018-07-17 23:20:16 +0900667 }
668 return null;
669 }
670
671 /**
Jian Li7f024de2018-07-07 03:51:02 +0900672 * Checks the validity of ARP mode.
673 *
674 * @param arpMode ARP mode
675 * @return returns true if the ARP mode is valid, false otherwise
676 */
677 public static boolean checkArpMode(String arpMode) {
678
679 if (isNullOrEmpty(arpMode)) {
680 return false;
681 } else {
682 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
683 }
684 }
685
686 /**
Jian Licad36c72018-09-13 17:44:54 +0900687 * Checks the validity of activation flag.
688 *
689 * @param activationFlag activation flag
690 * @return returns true if the activation flag is valid, false otherwise
691 */
692 public static boolean checkActivationFlag(String activationFlag) {
693
694 switch (activationFlag) {
695 case ENABLE:
696 return true;
697 case DISABLE:
698 return false;
699 default:
700 throw new IllegalArgumentException("The given activation flag is not valid!");
701 }
702 }
703
704 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900705 * Swaps current location with old location info.
706 * The revised instance port will be used to mod the flow rules after migration.
707 *
708 * @param instPort instance port
709 * @return location swapped instance port
710 */
711 public static InstancePort swapStaleLocation(InstancePort instPort) {
712 return DefaultInstancePort.builder()
713 .deviceId(instPort.oldDeviceId())
714 .portNumber(instPort.oldPortNumber())
715 .state(instPort.state())
716 .ipAddress(instPort.ipAddress())
717 .macAddress(instPort.macAddress())
718 .networkId(instPort.networkId())
719 .portId(instPort.portId())
720 .build();
721 }
722
723 /**
Daniel Park2ff66b42018-08-01 11:52:45 +0900724 * Compares two router interfaces are equal.
725 * Will be remove this after Openstack4j implements equals.
726 *
727 * @param routerInterface1 router interface
728 * @param routerInterface2 router interface
729 * @return returns true if two router interfaces are equal, false otherwise
730 */
Jian Li63430202018-08-30 16:24:09 +0900731 public static boolean routerInterfacesEquals(RouterInterface routerInterface1,
732 RouterInterface routerInterface2) {
Daniel Park2ff66b42018-08-01 11:52:45 +0900733 return Objects.equals(routerInterface1.getId(), routerInterface2.getId()) &&
734 Objects.equals(routerInterface1.getPortId(), routerInterface2.getPortId()) &&
735 Objects.equals(routerInterface1.getSubnetId(), routerInterface2.getSubnetId()) &&
736 Objects.equals(routerInterface1.getTenantId(), routerInterface2.getTenantId());
737 }
738
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900739 /**
740 * Returns the vnic type of given port.
741 *
742 * @param portName port name
743 * @return vnit type
744 */
Daniel Park7e8c4d82018-08-13 23:47:49 +0900745 public static VnicType vnicType(String portName) {
746 if (portName.startsWith(PORT_NAME_PREFIX_VM) ||
747 portName.startsWith(PORT_NAME_VHOST_USER_PREFIX_VM)) {
748 return VnicType.NORMAL;
749 } else if (isDirectPort(portName)) {
750 return VnicType.DIRECT;
751 } else {
752 return VnicType.UNSUPPORTED;
753 }
754 }
755
Jian Li63430202018-08-30 16:24:09 +0900756 /**
757 * Deserializes raw payload into HttpRequest object.
758 *
759 * @param rawData raw http payload
760 * @return HttpRequest object
761 */
762 public static HttpRequest parseHttpRequest(byte[] rawData) {
763 SessionInputBufferImpl sessionInputBuffer =
764 new SessionInputBufferImpl(
765 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
766 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900767 DefaultHttpRequestParser requestParser =
768 new DefaultHttpRequestParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900769 try {
770 return requestParser.parse();
771 } catch (IOException | HttpException e) {
772 log.warn("Failed to parse HttpRequest, due to {}", e);
773 }
774
775 return null;
776 }
777
778 /**
779 * Serializes HttpRequest object to byte array.
780 *
781 * @param request http request object
782 * @return byte array
783 */
784 public static byte[] unparseHttpRequest(HttpRequest request) {
785 try {
786 SessionOutputBufferImpl sessionOutputBuffer =
787 new SessionOutputBufferImpl(
788 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
789
790 ByteArrayOutputStream baos = new ByteArrayOutputStream();
791 sessionOutputBuffer.bind(baos);
792
Jian Li5ecfd1a2018-12-10 11:41:03 +0900793 HttpMessageWriter<HttpRequest> requestWriter =
794 new DefaultHttpRequestWriter(sessionOutputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900795 requestWriter.write(request);
796 sessionOutputBuffer.flush();
797
798 return baos.toByteArray();
799 } catch (HttpException | IOException e) {
800 log.warn("Failed to unparse HttpRequest, due to {}", e);
801 }
802
803 return null;
804 }
805
806 /**
807 * Deserializes raw payload into HttpResponse object.
808 *
809 * @param rawData raw http payload
810 * @return HttpResponse object
811 */
812 public static HttpResponse parseHttpResponse(byte[] rawData) {
813 SessionInputBufferImpl sessionInputBuffer =
814 new SessionInputBufferImpl(
815 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
816 sessionInputBuffer.bind(new ByteArrayInputStream(rawData));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900817 DefaultHttpResponseParser responseParser =
818 new DefaultHttpResponseParser(sessionInputBuffer);
Jian Li63430202018-08-30 16:24:09 +0900819 try {
820 return responseParser.parse();
821 } catch (IOException | HttpException e) {
822 log.warn("Failed to parse HttpResponse, due to {}", e);
823 }
824
825 return null;
826 }
827
828 /**
829 * Serializes HttpResponse header to byte array.
830 *
831 * @param response http response object
832 * @return byte array
833 */
834 public static byte[] unparseHttpResponseHeader(HttpResponse response) {
835 try {
836 SessionOutputBufferImpl sessionOutputBuffer =
837 new SessionOutputBufferImpl(
838 new HttpTransportMetricsImpl(), HTTP_PAYLOAD_BUFFER);
839
840 ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
841 sessionOutputBuffer.bind(headerBaos);
842
843 HttpMessageWriter<HttpResponse> responseWriter =
844 new DefaultHttpResponseWriter(sessionOutputBuffer);
845 responseWriter.write(response);
846 sessionOutputBuffer.flush();
847
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900848 log.debug(headerBaos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900849
850 return headerBaos.toByteArray();
851 } catch (IOException | HttpException e) {
852 log.warn("Failed to unparse HttpResponse headers, due to {}", e);
853 }
854
855 return null;
856 }
857
858 /**
859 * Serializes HttpResponse object to byte array.
860 *
861 * @param response http response object
862 * @return byte array
863 */
864 public static byte[] unparseHttpResponseBody(HttpResponse response) {
865 try {
866 ByteArrayOutputStream baos = new ByteArrayOutputStream();
867 response.getEntity().writeTo(baos);
868
869 log.debug(response.toString());
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900870 log.debug(baos.toString(Charsets.UTF_8.name()));
Jian Li63430202018-08-30 16:24:09 +0900871
872 return baos.toByteArray();
873 } catch (IOException e) {
874 log.warn("Failed to unparse HttpResponse, due to {}", e);
875 }
876
877 return null;
878 }
879
880 /**
881 * Encodes the given data using HmacSHA256 encryption method with given secret key.
882 *
883 * @param key secret key
884 * @param data data to be encrypted
885 * @return Hmac256 encrypted data
886 */
887 public static String hmacEncrypt(String key, String data) {
888 try {
889 Mac sha256Hmac = Mac.getInstance(HMAC_SHA256);
890 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA256);
891 sha256Hmac.init(secretKey);
892 return Hex.encodeHexString(sha256Hmac.doFinal(data.getBytes("UTF-8")));
893 } catch (Exception e) {
894 log.warn("Failed to encrypt data {} using key {}, due to {}", data, key, e);
895 }
896 return null;
897 }
898
Daniel Parka73c2362018-09-17 17:43:25 +0900899 /**
900 * Creates flow trace request string.
901 *
902 * @param srcIp src ip address
903 * @param dstIp dst ip address
904 * @param srcInstancePort src instance port
905 * @param osNetService openstack networking service
Daniel Park5aef9822018-09-20 18:04:18 +0900906 * @param uplink true if this request is for uplink
Daniel Parka73c2362018-09-17 17:43:25 +0900907 * @return flow trace request string
908 */
909 public static String traceRequestString(String srcIp,
910 String dstIp,
911 InstancePort srcInstancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900912 OpenstackNetworkService osNetService,
913 boolean uplink) {
Daniel Parka73c2362018-09-17 17:43:25 +0900914
915 StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
916
917 if (uplink) {
918
919 requestStringBuilder.append(COMMA)
920 .append(IN_PORT)
921 .append(srcInstancePort.portNumber().toString())
922 .append(COMMA)
923 .append(NW_SRC)
924 .append(srcIp)
925 .append(COMMA);
926
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900927 String modifiedDstIp = dstIp;
Jian Li621f73c2018-12-15 01:49:22 +0900928 Type netType = osNetService.networkType(srcInstancePort.networkId());
929 if (netType == Type.VXLAN || netType == Type.GRE ||
930 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900931 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900932 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900933 requestStringBuilder.append(DL_DST)
934 .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900935 } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(
936 IpAddress.valueOf(dstIp))) {
Daniel Parka73c2362018-09-17 17:43:25 +0900937 requestStringBuilder.append(DL_DST)
938 .append(DEFAULT_GATEWAY_MAC_STR)
939 .append(COMMA);
940 }
941 } else {
942 if (srcIp.equals(dstIp)) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900943 modifiedDstIp = osNetService.gatewayIp(srcInstancePort.portId());
Daniel Parka73c2362018-09-17 17:43:25 +0900944 }
945 }
946
947 requestStringBuilder.append(NW_DST)
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900948 .append(modifiedDstIp)
Daniel Parka73c2362018-09-17 17:43:25 +0900949 .append("\n");
950 } else {
951 requestStringBuilder.append(COMMA)
952 .append(NW_SRC)
953 .append(dstIp)
954 .append(COMMA);
955
Jian Li621f73c2018-12-15 01:49:22 +0900956 Type netType = osNetService.networkType(srcInstancePort.networkId());
957
958 if (netType == Type.VXLAN || netType == Type.GRE ||
959 netType == Type.VLAN || netType == Type.GENEVE) {
Daniel Parka73c2362018-09-17 17:43:25 +0900960 requestStringBuilder.append(TUN_ID)
961 .append(osNetService.segmentId(srcInstancePort.networkId()))
962 .append(COMMA);
963 }
964 requestStringBuilder.append(NW_DST)
965 .append(srcIp)
966 .append("\n");
Daniel Parka73c2362018-09-17 17:43:25 +0900967 }
968
969 return requestStringBuilder.toString();
970 }
971
972 /**
973 * Sends flow trace string to node.
974 *
975 * @param requestString reqeust string
976 * @param node src node
977 * @return flow trace result in string format
978 */
979 public static String sendTraceRequestToNode(String requestString,
980 OpenstackNode node) {
981 String traceResult = null;
982 OpenstackSshAuth sshAuth = node.sshAuthInfo();
983
984 try (SshClient client = SshClient.setUpDefaultClient()) {
985 client.start();
986
987 try (ClientSession session = client
988 .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
989 .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
990 session.addPasswordIdentity(sshAuth.password());
991 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
992
993
994 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
995
996 log.debug("requestString: {}", requestString);
997 final InputStream inputStream =
Daniel Parka3ffbdb2018-11-28 13:51:39 +0900998 new ByteArrayInputStream(requestString.getBytes(Charsets.UTF_8));
Daniel Parka73c2362018-09-17 17:43:25 +0900999
Jian Li5ecfd1a2018-12-10 11:41:03 +09001000 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Daniel Parka73c2362018-09-17 17:43:25 +09001001 OutputStream errStream = new ByteArrayOutputStream();
1002
1003 channel.setIn(new NoCloseInputStream(inputStream));
1004 channel.setErr(errStream);
1005 channel.setOut(outputStream);
1006
1007 Collection<ClientChannelEvent> eventList = Lists.newArrayList();
1008 eventList.add(ClientChannelEvent.OPENED);
1009
1010 OpenFuture channelFuture = channel.open();
1011
1012 if (channelFuture.await(TIMEOUT_MS, TimeUnit.SECONDS)) {
1013
1014 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
1015
1016 while (!channelFuture.isOpened()) {
1017 if ((timeoutExpiredMs - System.currentTimeMillis()) <= 0) {
1018 log.error("Failed to open channel");
1019 return null;
1020 }
1021 }
1022 TimeUnit.SECONDS.sleep(WAIT_OUTPUT_STREAM_SECOND);
1023
Jian Li5ecfd1a2018-12-10 11:41:03 +09001024 traceResult = outputStream.toString(Charsets.UTF_8.name());
Daniel Parka73c2362018-09-17 17:43:25 +09001025
1026 channel.close();
1027 }
1028 } finally {
1029 session.close();
1030 }
1031 } finally {
1032 client.stop();
1033 }
1034
1035 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001036 log.error("Exception occurred because of {}", e);
Daniel Parka73c2362018-09-17 17:43:25 +09001037 }
1038
1039 return traceResult;
1040 }
1041
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001042 /**
1043 * Returns the floating ip with supplied instance port.
1044 *
1045 * @param instancePort instance port
1046 * @param osRouterAdminService openstack router admin service
1047 * @return floating ip
1048 */
1049 public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001050 OpenstackRouterAdminService
1051 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001052 return osRouterAdminService.floatingIps().stream()
1053 .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
1054 .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
1055 .findAny().orElse(null);
1056 }
1057
1058 /**
1059 * Sends GARP packet with supplied floating ip information.
1060 *
1061 * @param floatingIP floating ip
1062 * @param instancePort instance port
1063 * @param vlanId vlain id
1064 * @param gatewayNode gateway node
1065 * @param packetService packet service
1066 */
Jian Li32b03622018-11-06 17:54:24 +09001067 public static void processGarpPacketForFloatingIp(NetFloatingIP floatingIP,
1068 InstancePort instancePort,
1069 VlanId vlanId,
1070 OpenstackNode gatewayNode,
1071 PacketService packetService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001072 Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
1073
1074 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1075 .setOutput(gatewayNode.uplinkPortNum()).build();
1076
1077 packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
1078 ByteBuffer.wrap(ethernet.serialize())));
1079 }
1080
1081 /**
1082 * Returns the external peer router with supplied network information.
1083 *
1084 * @param network network
1085 * @param osNetworkService openstack network service
1086 * @param osRouterAdminService openstack router admin service
1087 * @return external peer router
1088 */
1089 public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001090 OpenstackNetworkService
1091 osNetworkService,
1092 OpenstackRouterAdminService
1093 osRouterAdminService) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001094 if (network == null) {
1095 return null;
1096 }
1097
Jian Lie6e609f2019-05-14 17:45:54 +09001098 Subnet subnet = osNetworkService.subnets(network.getId())
1099 .stream().findAny().orElse(null);
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001100
1101 if (subnet == null) {
1102 return null;
1103 }
1104
1105 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
1106 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1107 .findAny().orElse(null);
1108 if (osRouterIface == null) {
1109 return null;
1110 }
1111
1112 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001113 if (osRouter == null || osRouter.getExternalGatewayInfo() == null) {
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001114 return null;
1115 }
1116
1117 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
1118 return osNetworkService.externalPeerRouter(exGatewayInfo);
1119
1120 }
1121
Jian Liebde74d2018-11-14 00:18:57 +09001122 /**
1123 * Returns the external peer router with specified subnet information.
1124 *
1125 * @param subnet openstack subnet
1126 * @param osRouterService openstack router service
1127 * @param osNetworkService openstack network service
1128 * @return external peer router
1129 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001130 public static ExternalPeerRouter externalPeerRouterFromSubnet(Subnet subnet,
1131 OpenstackRouterService
1132 osRouterService,
1133 OpenstackNetworkService
1134 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001135 Router osRouter = getRouterFromSubnet(subnet, osRouterService);
1136 if (osRouter == null) {
1137 return null;
1138 }
1139 if (osRouter.getExternalGatewayInfo() == null) {
1140 // this router does not have external connectivity
1141 log.trace("router({}) has no external gateway",
1142 osRouter.getName());
1143 return null;
1144 }
1145
1146 return osNetworkService.externalPeerRouter(osRouter.getExternalGatewayInfo());
1147 }
1148
1149 /**
1150 * Returns the external ip address with specified router information.
1151 *
1152 * @param srcSubnet source subnet
1153 * @param osRouterService openstack router service
1154 * @param osNetworkService openstack network service
1155 * @return external ip address
1156 */
1157 public static IpAddress externalIpFromSubnet(Subnet srcSubnet,
Jian Li5ecfd1a2018-12-10 11:41:03 +09001158 OpenstackRouterService
1159 osRouterService,
1160 OpenstackNetworkService
1161 osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001162
1163 Router osRouter = getRouterFromSubnet(srcSubnet, osRouterService);
1164
1165 if (osRouter.getExternalGatewayInfo() == null) {
1166 // this router does not have external connectivity
1167 log.trace("router({}) has no external gateway",
1168 osRouter.getName());
1169 return null;
1170 }
1171
1172 return getExternalIp(osRouter, osNetworkService);
1173 }
1174
1175 /**
1176 * Returns the external ip address with specified router information.
1177 *
1178 * @param router openstack router
1179 * @param osNetworkService openstack network service
1180 * @return external ip address
1181 */
Jian Li5ecfd1a2018-12-10 11:41:03 +09001182 public static IpAddress getExternalIp(Router router,
1183 OpenstackNetworkService osNetworkService) {
Jian Liebde74d2018-11-14 00:18:57 +09001184 if (router == null) {
1185 return null;
1186 }
1187
1188 ExternalGateway externalGateway = router.getExternalGatewayInfo();
1189 if (externalGateway == null || !externalGateway.isEnableSnat()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +09001190 log.trace("Failed to get externalIp for router {} because " +
1191 "externalGateway is null or SNAT is disabled",
Jian Liebde74d2018-11-14 00:18:57 +09001192 router.getId());
1193 return null;
1194 }
1195
1196 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
1197 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
1198 .stream()
1199 .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
1200 .findAny().orElse(null);
1201
1202 if (exGatewayPort == null) {
1203 return null;
1204 }
1205
1206 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
1207 .findAny().get().getIpAddress());
1208 }
1209
Jian Li2d68c192018-12-13 15:52:59 +09001210 /**
1211 * Returns the tunnel port number with specified net ID and openstack node.
1212 *
1213 * @param netId network ID
1214 * @param netService network service
1215 * @param osNode openstack node
1216 * @return tunnel port number
1217 */
1218 public static PortNumber tunnelPortNumByNetId(String netId,
1219 OpenstackNetworkService netService,
1220 OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +09001221 Type netType = netService.networkType(netId);
Jian Li2d68c192018-12-13 15:52:59 +09001222
1223 if (netType == null) {
1224 return null;
1225 }
1226
1227 return tunnelPortNumByNetType(netType, osNode);
1228 }
1229
1230 /**
1231 * Returns the tunnel port number with specified net type and openstack node.
1232 *
1233 * @param netType network type
1234 * @param osNode openstack node
1235 * @return tunnel port number
1236 */
SONA Project6bc5c4a2018-12-14 23:49:52 +09001237 public static PortNumber tunnelPortNumByNetType(Type netType, OpenstackNode osNode) {
Jian Li2d68c192018-12-13 15:52:59 +09001238 switch (netType) {
1239 case VXLAN:
1240 return osNode.vxlanTunnelPortNum();
1241 case GRE:
1242 return osNode.greTunnelPortNum();
Jian Li621f73c2018-12-15 01:49:22 +09001243 case GENEVE:
1244 return osNode.geneveTunnelPortNum();
Jian Li2d68c192018-12-13 15:52:59 +09001245 default:
1246 return null;
1247 }
1248 }
1249
Jian Li7b8c3682019-05-12 13:57:15 +09001250 /**
1251 * Returns the REST URL of active node.
1252 *
1253 * @param haService openstack HA service
1254 * @return REST URL of active node
1255 */
1256 public static String getActiveUrl(OpenstackHaService haService) {
1257 return "http://" + haService.getActiveIp().toString() + ":" +
1258 REST_PORT + "/" + OPENSTACK_NETWORKING_REST_PATH + "/";
1259 }
1260
1261 /**
1262 * Returns the REST client instance with given resource path.
1263 *
1264 * @param haService openstack HA service
1265 * @param resourcePath resource path
1266 * @return REST client instance
1267 */
1268 public static WebTarget getActiveClient(OpenstackHaService haService,
1269 String resourcePath) {
1270 HttpAuthenticationFeature feature =
1271 HttpAuthenticationFeature.universal(REST_USER, REST_PASSWORD);
1272 Client client = ClientBuilder.newClient().register(feature);
1273 return client.target(getActiveUrl(haService)).path(resourcePath);
1274 }
1275
1276 /**
1277 * Returns the post response from the active node.
1278 *
1279 * @param haService openstack HA service
1280 * @param resourcePath resource path
1281 * @param input input
1282 * @return post response
1283 */
1284 public static Response syncPost(OpenstackHaService haService,
1285 String resourcePath,
1286 String input) {
1287
1288 log.debug("Sync POST request with {} on {}",
1289 haService.getActiveIp().toString(), resourcePath);
1290
1291 return getActiveClient(haService, resourcePath)
1292 .request(APPLICATION_JSON_TYPE)
1293 .post(Entity.json(input));
1294 }
1295
1296 /**
1297 * Returns the put response from the active node.
1298 *
1299 * @param haService openstack HA service
1300 * @param resourcePath resource path
1301 * @param id resource identifier
1302 * @param input input
1303 * @return put response
1304 */
1305 public static Response syncPut(OpenstackHaService haService,
1306 String resourcePath,
1307 String id, String input) {
1308 return syncPut(haService, resourcePath, null, id, input);
1309 }
1310
1311 /**
1312 * Returns the put response from the active node.
1313 *
1314 * @param haService openstack HA service
1315 * @param resourcePath resource path
1316 * @param id resource identifier
1317 * @param suffix resource suffix
1318 * @param input input
1319 * @return put response
1320 */
1321 public static Response syncPut(OpenstackHaService haService,
1322 String resourcePath,
1323 String suffix,
1324 String id, String input) {
1325
1326 log.debug("Sync PUT request with {} on {}",
1327 haService.getActiveIp().toString(), resourcePath);
1328
1329 String pathStr = "/" + id;
1330
1331 if (suffix != null) {
1332 pathStr += "/" + suffix;
1333 }
1334
1335 return getActiveClient(haService, resourcePath)
1336 .path(pathStr)
1337 .request(APPLICATION_JSON_TYPE)
1338 .put(Entity.json(input));
1339 }
1340
1341 /**
1342 * Returns the delete response from the active node.
1343 *
1344 * @param haService openstack HA service
1345 * @param resourcePath resource path
1346 * @param id resource identifier
1347 * @return delete response
1348 */
1349 public static Response syncDelete(OpenstackHaService haService,
1350 String resourcePath,
1351 String id) {
1352
1353 log.debug("Sync DELETE request with {} on {}",
1354 haService.getActiveIp().toString(), resourcePath);
1355
1356 return getActiveClient(haService, resourcePath)
1357 .path("/" + id)
1358 .request(APPLICATION_JSON_TYPE)
1359 .delete();
1360 }
1361
Jian Li51728702019-05-17 18:38:56 +09001362 /**
1363 * Gets the ovsdb client with supplied openstack node.
1364 *
1365 * @param node openstack node
1366 * @param ovsdbPort openvswitch DB port number
1367 * @param controller openvswitch DB controller instance
1368 * @return ovsdb client instance
1369 */
1370 public static OvsdbClientService getOvsdbClient(OpenstackNode node, int ovsdbPort,
1371 OvsdbController controller) {
1372 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
1373 return controller.getOvsdbClient(ovsdb);
1374 }
1375
1376 /**
1377 * Obtains the name of interface attached to the openstack VM.
1378 *
1379 * @param portId openstack port identifier
1380 * @return name of interface
1381 */
1382 public static String ifaceNameFromOsPortId(String portId) {
1383 if (portId != null) {
1384 return PORT_NAME_PREFIX_VM + StringUtils.substring(portId, 0, TAP_PORT_LENGTH);
1385 }
1386
1387 return null;
1388 }
1389
Jian Li5ecfd1a2018-12-10 11:41:03 +09001390 private static Router getRouterFromSubnet(Subnet subnet,
1391 OpenstackRouterService osRouterService) {
Jian Liebde74d2018-11-14 00:18:57 +09001392 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
1393 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
1394 .findAny().orElse(null);
1395 if (osRouterIface == null) {
1396 return null;
1397 }
1398
1399 return osRouterService.router(osRouterIface.getId());
1400 }
1401
Daniel Park7e8c4d82018-08-13 23:47:49 +09001402 private static boolean isDirectPort(String portName) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001403 return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
Daniel Park7e8c4d82018-08-13 23:47:49 +09001404 }
1405
Daniel Park2ff66b42018-08-01 11:52:45 +09001406 /**
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001407 * Returns GARP packet with supplied floating ip and instance port information.
1408 *
1409 * @param floatingIP floating ip
1410 * @param instancePort instance port
1411 * @param vlanId vlan id
1412 * @return GARP packet
1413 */
1414 private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
1415 InstancePort instancePort,
1416 VlanId vlanId) {
1417 Ethernet ethernet = new Ethernet();
1418 ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
1419 ethernet.setSourceMACAddress(instancePort.macAddress());
1420 ethernet.setEtherType(Ethernet.TYPE_ARP);
1421 ethernet.setVlanID(vlanId.id());
1422
1423 ARP arp = new ARP();
1424 arp.setOpCode(ARP.OP_REPLY);
1425 arp.setProtocolType(ARP.PROTO_TYPE_IP);
1426 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
1427
1428 arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
1429 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
1430
1431 arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
1432 arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
1433
Jian Li5ecfd1a2018-12-10 11:41:03 +09001434 arp.setSenderProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
1435 arp.setTargetProtocolAddress(valueOf(floatingIP.getFloatingIpAddress()).toInt());
Daniel Park4fa1f5e2018-10-17 12:41:52 +09001436
1437 ethernet.setPayload(arp);
1438
1439 return ethernet;
1440 }
1441
1442 /**
Jian Lia271b3c2019-09-03 23:10:20 +09001443 * Re-structures the OVS port name.
1444 * The length of OVS port name should be not large than 15.
1445 *
1446 * @param portName original port name
1447 * @return re-structured OVS port name
1448 */
1449 public static String structurePortName(String portName) {
1450
1451 // The size of OVS port name should not be larger than 15
1452 if (portName.length() > PORT_NAME_MAX_LENGTH) {
1453 return StringUtils.substring(portName, 0, PORT_NAME_MAX_LENGTH);
1454 }
1455
1456 return portName;
1457 }
1458
1459 /**
Jian Lid5727622019-09-11 11:15:16 +09001460 * Obtains flow group key from the given id.
1461 *
1462 * @param groupId flow group identifier
1463 * @return flow group key
1464 */
1465 public static GroupKey getGroupKey(int groupId) {
1466 return new DefaultGroupKey((Integer.toString(groupId)).getBytes());
1467 }
1468
1469 /**
Jian Li51b844c2018-05-31 10:59:03 +09001470 * Builds up and a complete endpoint URL from gateway node.
1471 *
1472 * @param node gateway node
1473 * @return a complete endpoint URL
1474 */
1475 private static String buildEndpoint(OpenstackNode node) {
1476
Jian Lic704b672018-09-04 18:52:53 +09001477 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +09001478
1479 StringBuilder endpointSb = new StringBuilder();
1480 endpointSb.append(auth.protocol().name().toLowerCase());
1481 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +09001482 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +09001483 return endpointSb.toString();
1484 }
1485
1486 /**
1487 * Obtains the SSL config without verifying the certification.
1488 *
1489 * @return SSL config
1490 */
1491 private static Config getSslConfig() {
1492 // we bypass the SSL certification verification for now
1493 // TODO: verify server side SSL using a given certification
1494 Config config = Config.newConfig().withSSLVerificationDisabled();
1495
1496 TrustManager[] trustAllCerts = new TrustManager[]{
1497 new X509TrustManager() {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001498 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001499 public X509Certificate[] getAcceptedIssuers() {
1500 return null;
1501 }
1502
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001503 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001504 public void checkClientTrusted(X509Certificate[] certs,
1505 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001506 return;
Jian Li51b844c2018-05-31 10:59:03 +09001507 }
1508
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001509 @Override
Jian Li51b844c2018-05-31 10:59:03 +09001510 public void checkServerTrusted(X509Certificate[] certs,
1511 String authType) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001512 return;
Jian Li51b844c2018-05-31 10:59:03 +09001513 }
1514 }
1515 };
1516
1517 HostnameVerifier allHostsValid = (hostname, session) -> true;
1518
1519 try {
1520 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
1521 sc.init(null, trustAllCerts,
1522 new java.security.SecureRandom());
1523 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
1524 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
1525
1526 config.withSSLContext(sc);
1527 } catch (Exception e) {
Daniel Parka3ffbdb2018-11-28 13:51:39 +09001528 log.error("Failed to access OpenStack service due to {}", e);
Jian Li51b844c2018-05-31 10:59:03 +09001529 return null;
1530 }
1531
1532 return config;
1533 }
1534
1535 /**
1536 * Obtains the facing object with given openstack perspective.
1537 *
1538 * @param perspective keystone perspective
1539 * @return facing object
1540 */
1541 private static Facing getFacing(Perspective perspective) {
1542
1543 switch (perspective) {
1544 case PUBLIC:
1545 return Facing.PUBLIC;
1546 case ADMIN:
1547 return Facing.ADMIN;
1548 case INTERNAL:
1549 return Facing.INTERNAL;
1550 default:
1551 return null;
1552 }
1553 }
1554
1555 /**
1556 * Obtains gateway instance by giving index number.
1557 *
1558 * @param gws a collection of gateway nodes
1559 * @param index index number
1560 * @return gateway instance
1561 */
1562 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
1563 Map<String, OpenstackNode> hashMap = new HashMap<>();
1564 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
1565 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
1566 Iterator<String> iteratorKey = treeMap.keySet().iterator();
1567
1568 int intIndex = 0;
1569 OpenstackNode gw = null;
1570 while (iteratorKey.hasNext()) {
1571 String key = iteratorKey.next();
1572
1573 if (intIndex == index) {
1574 gw = treeMap.get(key);
1575 }
1576 intIndex++;
1577 }
1578 return gw;
1579 }
Jian Li40f032a2019-10-02 20:36:09 +09001580
1581 private static void print(String format, Object... args) {
1582 System.out.println(String.format(format, args));
1583 }
Jian Li63430202018-08-30 16:24:09 +09001584}