blob: 0253e4d456eeab730f7399808fd6291f9a568eea [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
18import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.ObjectMapper;
Jian Lieb9f77d2018-02-20 11:25:45 +090020import com.fasterxml.jackson.databind.node.ObjectNode;
Jian Li24ec59f2018-05-23 19:01:25 +090021import com.google.common.base.Strings;
Jian Li7f70bb72018-07-06 23:35:30 +090022import org.onosproject.cfg.ConfigProperty;
Jian Li1064e4f2018-05-29 16:16:53 +090023import org.onosproject.net.DeviceId;
Daniel Park95f73312018-07-31 15:48:34 +090024import org.onosproject.net.device.DeviceService;
Jian Lia171a432018-06-11 11:52:11 +090025import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090026import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Li7f70bb72018-07-06 23:35:30 +090027import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Jian Liec5c32b2018-07-13 14:28:58 +090028import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090029import org.onosproject.openstacknode.api.OpenstackAuth;
30import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090031import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li51b844c2018-05-31 10:59:03 +090032import org.openstack4j.api.OSClient;
33import org.openstack4j.api.client.IOSClientBuilder;
34import org.openstack4j.api.exceptions.AuthenticationException;
35import org.openstack4j.api.types.Facing;
36import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090037import org.openstack4j.core.transport.ObjectMapperSingleton;
38import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090039import org.openstack4j.model.common.Identifier;
Jian Li24ec59f2018-05-23 19:01:25 +090040import org.openstack4j.model.network.NetFloatingIP;
41import org.openstack4j.model.network.Network;
Jian Lia171a432018-06-11 11:52:11 +090042import org.openstack4j.model.network.Port;
Jian Li0b564282018-06-20 00:50:53 +090043import org.openstack4j.model.network.RouterInterface;
Jian Li51b844c2018-05-31 10:59:03 +090044import org.openstack4j.openstack.OSFactory;
Jian Li0b564282018-06-20 00:50:53 +090045import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
Jian Li091d8d22018-02-20 10:42:06 +090046import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
Jian Li51b844c2018-05-31 10:59:03 +090049import javax.net.ssl.HostnameVerifier;
50import javax.net.ssl.HttpsURLConnection;
51import javax.net.ssl.SSLContext;
52import javax.net.ssl.TrustManager;
53import javax.net.ssl.X509TrustManager;
Jian Li0b564282018-06-20 00:50:53 +090054import java.io.IOException;
Jian Li091d8d22018-02-20 10:42:06 +090055import java.io.InputStream;
Jian Li51b844c2018-05-31 10:59:03 +090056import java.security.cert.X509Certificate;
Jian Li1064e4f2018-05-29 16:16:53 +090057import java.util.HashMap;
58import java.util.Iterator;
59import java.util.Map;
Daniel Park95f73312018-07-31 15:48:34 +090060import java.util.Objects;
Jian Li7f70bb72018-07-06 23:35:30 +090061import java.util.Optional;
Jian Li1064e4f2018-05-29 16:16:53 +090062import java.util.Set;
63import java.util.TreeMap;
Jian Li091d8d22018-02-20 10:42:06 +090064
65import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Park95f73312018-07-31 15:48:34 +090066import static com.google.common.base.Preconditions.checkNotNull;
Jian Li7f024de2018-07-07 03:51:02 +090067import static com.google.common.base.Strings.isNullOrEmpty;
Daniel Park95f73312018-07-31 15:48:34 +090068import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parkc4d06402018-05-28 15:57:37 +090069import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
70import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Ray Milkey9dc57392018-06-08 08:52:31 -070071import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li0b564282018-06-20 00:50:53 +090072import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
Jian Li091d8d22018-02-20 10:42:06 +090073
74/**
75 * An utility that used in openstack networking app.
76 */
Jian Lidea0fdb2018-04-02 19:02:48 +090077public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +090078
Daniel Park95985382018-07-23 11:38:07 +090079 private static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +090080
Daniel Parkc4d06402018-05-28 15:57:37 +090081 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +090082 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +090083 private static final String PREFIX_DEVICE_NUMBER = "s";
84 private static final String PREFIX_FUNCTION_NUMBER = "f";
85
Jian Li51b844c2018-05-31 10:59:03 +090086 // keystone endpoint related variables
87 private static final String DOMAIN_DEFAULT = "default";
88 private static final String KEYSTONE_V2 = "v2.0";
89 private static final String KEYSTONE_V3 = "v3";
90 private static final String IDENTITY_PATH = "identity/";
91 private static final String SSL_TYPE = "SSL";
92
Jian Li7f024de2018-07-07 03:51:02 +090093 private static final String PROXY_MODE = "proxy";
94 private static final String BROADCAST_MODE = "broadcast";
95
Jian Li24ec59f2018-05-23 19:01:25 +090096 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
97
Jian Li091d8d22018-02-20 10:42:06 +090098 /**
99 * Prevents object instantiation from external.
100 */
Jian Lidea0fdb2018-04-02 19:02:48 +0900101 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +0900102 }
103
104 /**
105 * Interprets JSON string to corresponding openstack model entity object.
106 *
107 * @param input JSON string
108 * @param entityClazz openstack model entity class
109 * @return openstack model entity object
110 */
111 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
112 ObjectMapper mapper = new ObjectMapper();
113 try {
114 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
115 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
116 return ObjectMapperSingleton.getContext(entityClazz)
117 .readerFor(entityClazz)
118 .readValue(jsonTree);
119 } catch (Exception e) {
120 throw new IllegalArgumentException();
121 }
122 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900123
124 /**
125 * Converts openstack model entity object into JSON object.
126 *
127 * @param entity openstack model entity object
128 * @param entityClazz openstack model entity class
129 * @return JSON object
130 */
131 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
132 ObjectMapper mapper = new ObjectMapper();
133 try {
134 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
135 .writerFor(entityClazz)
136 .writeValueAsString(entity);
137 log.trace(strModelEntity);
138 return (ObjectNode) mapper.readTree(strModelEntity.getBytes());
Daniel Park95985382018-07-23 11:38:07 +0900139 } catch (IOException e) {
140 log.error("IOException occurred because of {}", e.toString());
Jian Lieb9f77d2018-02-20 11:25:45 +0900141 throw new IllegalStateException();
142 }
143 }
Jian Li1064e4f2018-05-29 16:16:53 +0900144
145 /**
Jian Li24ec59f2018-05-23 19:01:25 +0900146 * Obtains a floating IP associated with the given instance port.
147 *
148 * @param port instance port
149 * @param fips a collection of floating IPs
150 * @return associated floating IP
151 */
152 public static NetFloatingIP associatedFloatingIp(InstancePort port,
153 Set<NetFloatingIP> fips) {
Daniel Park95985382018-07-23 11:38:07 +0900154 try {
155 for (NetFloatingIP fip : fips) {
156 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
157 continue;
158 }
159 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
160 continue;
161 }
162 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
163 return fip;
164 }
Jian Li24ec59f2018-05-23 19:01:25 +0900165 }
Daniel Park95985382018-07-23 11:38:07 +0900166 } catch (NullPointerException e) {
167 log.error("Exception occurred because of {}", e.toString());
168 throw new NullPointerException();
Jian Li24ec59f2018-05-23 19:01:25 +0900169 }
170 return null;
171 }
172
173 /**
174 * Checks whether the given floating IP is associated with a VM.
175 *
176 * @param service openstack network service
177 * @param fip floating IP
178 * @return true if the given floating IP associated with a VM, false otherwise
179 */
180 public static boolean isAssociatedWithVM(OpenstackNetworkService service,
181 NetFloatingIP fip) {
182 Port osPort = service.port(fip.getPortId());
183 if (osPort == null) {
184 return false;
185 }
186
187 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
188 Network osNet = service.network(osPort.getNetworkId());
189 if (osNet == null) {
190 final String errorFormat = ERR_FLOW + "no network(%s) exists";
191 final String error = String.format(errorFormat,
192 fip.getFloatingIpAddress(), osPort.getNetworkId());
193 throw new IllegalStateException(error);
194 }
195 return true;
196 } else {
197 return false;
198 }
199 }
200
201 /**
Jian Lia171a432018-06-11 11:52:11 +0900202 * Obtains the gateway node by instance port.
203 *
204 * @param gateways a collection of gateway nodes
205 * @param instPort instance port
206 * @return a gateway node
207 */
208 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
209 InstancePort instPort) {
210 OpenstackNode gw = null;
211 if (instPort != null && instPort.deviceId() != null) {
212 gw = getGwByComputeDevId(gateways, instPort.deviceId());
213 }
214 return gw;
215 }
216
217 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900218 * Obtains the gateway node by device in compute node. Note that the gateway
219 * node is determined by device's device identifier.
220 *
221 * @param gws a collection of gateway nodes
222 * @param deviceId device identifier
223 * @return a gateway node
224 */
225 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws, DeviceId deviceId) {
226 int numOfGw = gws.size();
227
228 if (numOfGw == 0) {
229 return null;
230 }
231
232 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
233
234 return getGwByIndex(gws, gwIndex);
235 }
236
Jian Li51b844c2018-05-31 10:59:03 +0900237 /**
238 * Obtains a connected openstack client.
239 *
240 * @param osNode openstack node
241 * @return a connected openstack client
242 */
243 public static OSClient getConnectedClient(OpenstackNode osNode) {
244 OpenstackAuth auth = osNode.authentication();
245 String endpoint = buildEndpoint(osNode);
246 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900247
Jian Li51b844c2018-05-31 10:59:03 +0900248 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900249
Jian Li51b844c2018-05-31 10:59:03 +0900250 try {
251 if (endpoint.contains(KEYSTONE_V2)) {
252 IOSClientBuilder.V2 builder = OSFactory.builderV2()
253 .endpoint(endpoint)
254 .tenantName(auth.project())
255 .credentials(auth.username(), auth.password())
256 .withConfig(config);
257
258 if (perspective != null) {
259 builder.perspective(getFacing(perspective));
260 }
261
262 return builder.authenticate();
263 } else if (endpoint.contains(KEYSTONE_V3)) {
264
265 Identifier project = Identifier.byName(auth.project());
266 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
267
268 IOSClientBuilder.V3 builder = OSFactory.builderV3()
269 .endpoint(endpoint)
270 .credentials(auth.username(), auth.password(), domain)
271 .scopeToProject(project, domain)
272 .withConfig(config);
273
274 if (perspective != null) {
275 builder.perspective(getFacing(perspective));
276 }
277
278 return builder.authenticate();
279 } else {
280 log.warn("Unrecognized keystone version type");
281 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900282 }
Jian Li51b844c2018-05-31 10:59:03 +0900283 } catch (AuthenticationException e) {
284 log.error("Authentication failed due to {}", e.toString());
285 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900286 }
Jian Li1064e4f2018-05-29 16:16:53 +0900287 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900288
289 /**
290 * Extract the interface name with the supplied port.
291 *
292 * @param port port
293 * @return interface name
294 */
295 public static String getIntfNameFromPciAddress(Port port) {
Daniel Park95985382018-07-23 11:38:07 +0900296 if (port.getProfile() == null || port.getProfile().isEmpty()) {
Jian Li51b844c2018-05-31 10:59:03 +0900297 log.error("Port profile is not found");
298 return null;
299 }
300
Daniel Park95985382018-07-23 11:38:07 +0900301 if (!port.getProfile().containsKey(PCISLOT) ||
302 Strings.isNullOrEmpty(port.getProfile().get(PCISLOT).toString())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900303 log.error("Failed to retrieve the interface name because of no pci_slot information from the port");
304 return null;
305 }
Jian Li51b844c2018-05-31 10:59:03 +0900306
Daniel Parkc4d06402018-05-28 15:57:37 +0900307 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
308 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
309
310 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
311 .split(":")[2]
312 .split("\\.")[0];
313 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
314
315 String functionNumHex = port.getProfile().get(PCISLOT).toString()
316 .split(":")[2]
317 .split("\\.")[1];
318 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
319
320 String intfName;
321
322 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
323
Daniel Park95985382018-07-23 11:38:07 +0900324 if (!portNamePrefixMap().containsKey(vendorInfoForPort)) {
325 log.error("Failed to retrieve the interface name because of no port name prefix for vendor ID {}",
326 vendorInfoForPort);
Daniel Parkc4d06402018-05-28 15:57:37 +0900327 return null;
328 }
Ray Milkey9dc57392018-06-08 08:52:31 -0700329 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
Jian Li51b844c2018-05-31 10:59:03 +0900330
Daniel Parkc4d06402018-05-28 15:57:37 +0900331 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
332 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
333 } else {
334 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
335 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
336 }
337
338 return intfName;
339 }
Jian Li51b844c2018-05-31 10:59:03 +0900340
341 /**
Daniel Park95f73312018-07-31 15:48:34 +0900342 * Check if the given interface is added to the given device or not.
343 *
344 * @param deviceId device ID
345 * @param intfName interface name
346 * @param deviceService device service
347 * @return true if the given interface is added to the given device or false otherwise
348 */
349 public static boolean hasIntfAleadyInDevice(DeviceId deviceId, String intfName, DeviceService deviceService) {
350 checkNotNull(deviceId);
351 checkNotNull(intfName);
352
353 return deviceService.getPorts(deviceId).stream()
354 .anyMatch(port -> Objects.equals(port.annotations().value(PORT_NAME), intfName));
355 }
356
357 /**
Jian Li0b564282018-06-20 00:50:53 +0900358 * Adds router interfaces to openstack admin service.
359 * TODO fix the logic to add router interface to router
360 *
361 * @param osPort port
362 * @param adminService openstack admin service
363 */
364 public static void addRouterIface(Port osPort, OpenstackRouterAdminService adminService) {
365 osPort.getFixedIps().forEach(p -> {
366 JsonNode jsonTree = new ObjectMapper().createObjectNode()
367 .put("id", osPort.getDeviceId())
368 .put("tenant_id", osPort.getTenantId())
369 .put("subnet_id", p.getSubnetId())
370 .put("port_id", osPort.getId());
371 try {
372 RouterInterface rIface = getContext(NeutronRouterInterface.class)
373 .readerFor(NeutronRouterInterface.class)
374 .readValue(jsonTree);
375 if (adminService.routerInterface(rIface.getPortId()) != null) {
376 adminService.updateRouterInterface(rIface);
377 } else {
378 adminService.addRouterInterface(rIface);
379 }
380 } catch (IOException ignore) {
381 }
382 });
383 }
384
385 /**
Jian Li7f70bb72018-07-06 23:35:30 +0900386 * Obtains the property value with specified property key name.
387 *
388 * @param properties a collection of properties
389 * @param name key name
390 * @return mapping value
391 */
392 public static String getPropertyValue(Set<ConfigProperty> properties, String name) {
393 Optional<ConfigProperty> property =
394 properties.stream().filter(p -> p.name().equals(name)).findFirst();
395 return property.map(ConfigProperty::value).orElse(null);
396 }
397
398 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900399 * Prints out the JSON string in pretty format.
400 *
401 * @param mapper Object mapper
402 * @param jsonString JSON string
403 * @return pretty formatted JSON string
404 */
405 public static String prettyJson(ObjectMapper mapper, String jsonString) {
406 try {
407 Object jsonObject = mapper.readValue(jsonString, Object.class);
408 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
409 } catch (IOException e) {
410 log.debug("Json string parsing exception caused by {}", e);
411 }
412 return null;
413 }
414
415 /**
Jian Li7f024de2018-07-07 03:51:02 +0900416 * Checks the validity of ARP mode.
417 *
418 * @param arpMode ARP mode
419 * @return returns true if the ARP mode is valid, false otherwise
420 */
421 public static boolean checkArpMode(String arpMode) {
422
423 if (isNullOrEmpty(arpMode)) {
424 return false;
425 } else {
426 return arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE);
427 }
428 }
429
430 /**
Jian Liec5c32b2018-07-13 14:28:58 +0900431 * Swaps current location with old location info.
432 * The revised instance port will be used to mod the flow rules after migration.
433 *
434 * @param instPort instance port
435 * @return location swapped instance port
436 */
437 public static InstancePort swapStaleLocation(InstancePort instPort) {
438 return DefaultInstancePort.builder()
439 .deviceId(instPort.oldDeviceId())
440 .portNumber(instPort.oldPortNumber())
441 .state(instPort.state())
442 .ipAddress(instPort.ipAddress())
443 .macAddress(instPort.macAddress())
444 .networkId(instPort.networkId())
445 .portId(instPort.portId())
446 .build();
447 }
448
449 /**
Jian Li51b844c2018-05-31 10:59:03 +0900450 * Builds up and a complete endpoint URL from gateway node.
451 *
452 * @param node gateway node
453 * @return a complete endpoint URL
454 */
455 private static String buildEndpoint(OpenstackNode node) {
456
457 OpenstackAuth auth = node.authentication();
458
459 StringBuilder endpointSb = new StringBuilder();
460 endpointSb.append(auth.protocol().name().toLowerCase());
461 endpointSb.append("://");
462 endpointSb.append(node.endPoint());
463 endpointSb.append(":");
464 endpointSb.append(auth.port());
465 endpointSb.append("/");
466
467 // in case the version is v3, we need to append identity path into endpoint
468 if (auth.version().equals(KEYSTONE_V3)) {
469 endpointSb.append(IDENTITY_PATH);
470 }
471
472 endpointSb.append(auth.version());
473 return endpointSb.toString();
474 }
475
476 /**
477 * Obtains the SSL config without verifying the certification.
478 *
479 * @return SSL config
480 */
481 private static Config getSslConfig() {
482 // we bypass the SSL certification verification for now
483 // TODO: verify server side SSL using a given certification
484 Config config = Config.newConfig().withSSLVerificationDisabled();
485
486 TrustManager[] trustAllCerts = new TrustManager[]{
487 new X509TrustManager() {
488 public X509Certificate[] getAcceptedIssuers() {
489 return null;
490 }
491
492 public void checkClientTrusted(X509Certificate[] certs,
493 String authType) {
494 }
495
496 public void checkServerTrusted(X509Certificate[] certs,
497 String authType) {
498 }
499 }
500 };
501
502 HostnameVerifier allHostsValid = (hostname, session) -> true;
503
504 try {
505 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
506 sc.init(null, trustAllCerts,
507 new java.security.SecureRandom());
508 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
509 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
510
511 config.withSSLContext(sc);
512 } catch (Exception e) {
513 log.error("Failed to access OpenStack service due to {}", e.toString());
514 return null;
515 }
516
517 return config;
518 }
519
520 /**
521 * Obtains the facing object with given openstack perspective.
522 *
523 * @param perspective keystone perspective
524 * @return facing object
525 */
526 private static Facing getFacing(Perspective perspective) {
527
528 switch (perspective) {
529 case PUBLIC:
530 return Facing.PUBLIC;
531 case ADMIN:
532 return Facing.ADMIN;
533 case INTERNAL:
534 return Facing.INTERNAL;
535 default:
536 return null;
537 }
538 }
539
540 /**
541 * Obtains gateway instance by giving index number.
542 *
543 * @param gws a collection of gateway nodes
544 * @param index index number
545 * @return gateway instance
546 */
547 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
548 Map<String, OpenstackNode> hashMap = new HashMap<>();
549 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
550 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
551 Iterator<String> iteratorKey = treeMap.keySet().iterator();
552
553 int intIndex = 0;
554 OpenstackNode gw = null;
555 while (iteratorKey.hasNext()) {
556 String key = iteratorKey.next();
557
558 if (intIndex == index) {
559 gw = treeMap.get(key);
560 }
561 intIndex++;
562 }
563 return gw;
564 }
Jian Li091d8d22018-02-20 10:42:06 +0900565}