blob: 6a429025c93a743a735ace1e1675c5d8040fac8c [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 Li1064e4f2018-05-29 16:16:53 +090021import org.onosproject.net.DeviceId;
Jian Lia171a432018-06-11 11:52:11 +090022import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li51b844c2018-05-31 10:59:03 +090023import org.onosproject.openstacknode.api.OpenstackAuth;
24import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090025import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li51b844c2018-05-31 10:59:03 +090026import org.openstack4j.api.OSClient;
27import org.openstack4j.api.client.IOSClientBuilder;
28import org.openstack4j.api.exceptions.AuthenticationException;
29import org.openstack4j.api.types.Facing;
30import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090031import org.openstack4j.core.transport.ObjectMapperSingleton;
32import org.openstack4j.model.ModelEntity;
Jian Li51b844c2018-05-31 10:59:03 +090033import org.openstack4j.model.common.Identifier;
Jian Lia171a432018-06-11 11:52:11 +090034import org.openstack4j.model.network.Port;
Jian Li51b844c2018-05-31 10:59:03 +090035import org.openstack4j.openstack.OSFactory;
Jian Li091d8d22018-02-20 10:42:06 +090036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
Jian Li51b844c2018-05-31 10:59:03 +090039import javax.net.ssl.HostnameVerifier;
40import javax.net.ssl.HttpsURLConnection;
41import javax.net.ssl.SSLContext;
42import javax.net.ssl.TrustManager;
43import javax.net.ssl.X509TrustManager;
Jian Li091d8d22018-02-20 10:42:06 +090044import java.io.InputStream;
Jian Li51b844c2018-05-31 10:59:03 +090045import java.security.cert.X509Certificate;
Jian Li1064e4f2018-05-29 16:16:53 +090046import java.util.HashMap;
47import java.util.Iterator;
48import java.util.Map;
49import java.util.Set;
50import java.util.TreeMap;
Jian Li091d8d22018-02-20 10:42:06 +090051
52import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Parkc4d06402018-05-28 15:57:37 +090053import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
54import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
Ray Milkey9dc57392018-06-08 08:52:31 -070055import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li091d8d22018-02-20 10:42:06 +090056
57/**
58 * An utility that used in openstack networking app.
59 */
Jian Lidea0fdb2018-04-02 19:02:48 +090060public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +090061
Jian Lidea0fdb2018-04-02 19:02:48 +090062 protected static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +090063
Daniel Parkc4d06402018-05-28 15:57:37 +090064 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +090065 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +090066 private static final String PREFIX_DEVICE_NUMBER = "s";
67 private static final String PREFIX_FUNCTION_NUMBER = "f";
68
Jian Li51b844c2018-05-31 10:59:03 +090069 // keystone endpoint related variables
70 private static final String DOMAIN_DEFAULT = "default";
71 private static final String KEYSTONE_V2 = "v2.0";
72 private static final String KEYSTONE_V3 = "v3";
73 private static final String IDENTITY_PATH = "identity/";
74 private static final String SSL_TYPE = "SSL";
75
Jian Li091d8d22018-02-20 10:42:06 +090076 /**
77 * Prevents object instantiation from external.
78 */
Jian Lidea0fdb2018-04-02 19:02:48 +090079 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +090080 }
81
82 /**
83 * Interprets JSON string to corresponding openstack model entity object.
84 *
85 * @param input JSON string
86 * @param entityClazz openstack model entity class
87 * @return openstack model entity object
88 */
89 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
90 ObjectMapper mapper = new ObjectMapper();
91 try {
92 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
93 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
94 return ObjectMapperSingleton.getContext(entityClazz)
95 .readerFor(entityClazz)
96 .readValue(jsonTree);
97 } catch (Exception e) {
98 throw new IllegalArgumentException();
99 }
100 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900101
102 /**
103 * Converts openstack model entity object into JSON object.
104 *
105 * @param entity openstack model entity object
106 * @param entityClazz openstack model entity class
107 * @return JSON object
108 */
109 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
110 ObjectMapper mapper = new ObjectMapper();
111 try {
112 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
113 .writerFor(entityClazz)
114 .writeValueAsString(entity);
115 log.trace(strModelEntity);
116 return (ObjectNode) mapper.readTree(strModelEntity.getBytes());
117 } catch (Exception e) {
118 throw new IllegalStateException();
119 }
120 }
Jian Li1064e4f2018-05-29 16:16:53 +0900121
122 /**
Jian Lia171a432018-06-11 11:52:11 +0900123 * Obtains the gateway node by instance port.
124 *
125 * @param gateways a collection of gateway nodes
126 * @param instPort instance port
127 * @return a gateway node
128 */
129 public static OpenstackNode getGwByInstancePort(Set<OpenstackNode> gateways,
130 InstancePort instPort) {
131 OpenstackNode gw = null;
132 if (instPort != null && instPort.deviceId() != null) {
133 gw = getGwByComputeDevId(gateways, instPort.deviceId());
134 }
135 return gw;
136 }
137
138 /**
Jian Li1064e4f2018-05-29 16:16:53 +0900139 * Obtains the gateway node by device in compute node. Note that the gateway
140 * node is determined by device's device identifier.
141 *
142 * @param gws a collection of gateway nodes
143 * @param deviceId device identifier
144 * @return a gateway node
145 */
146 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws, DeviceId deviceId) {
147 int numOfGw = gws.size();
148
149 if (numOfGw == 0) {
150 return null;
151 }
152
153 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
154
155 return getGwByIndex(gws, gwIndex);
156 }
157
Jian Li51b844c2018-05-31 10:59:03 +0900158 /**
159 * Obtains a connected openstack client.
160 *
161 * @param osNode openstack node
162 * @return a connected openstack client
163 */
164 public static OSClient getConnectedClient(OpenstackNode osNode) {
165 OpenstackAuth auth = osNode.authentication();
166 String endpoint = buildEndpoint(osNode);
167 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900168
Jian Li51b844c2018-05-31 10:59:03 +0900169 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900170
Jian Li51b844c2018-05-31 10:59:03 +0900171 try {
172 if (endpoint.contains(KEYSTONE_V2)) {
173 IOSClientBuilder.V2 builder = OSFactory.builderV2()
174 .endpoint(endpoint)
175 .tenantName(auth.project())
176 .credentials(auth.username(), auth.password())
177 .withConfig(config);
178
179 if (perspective != null) {
180 builder.perspective(getFacing(perspective));
181 }
182
183 return builder.authenticate();
184 } else if (endpoint.contains(KEYSTONE_V3)) {
185
186 Identifier project = Identifier.byName(auth.project());
187 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
188
189 IOSClientBuilder.V3 builder = OSFactory.builderV3()
190 .endpoint(endpoint)
191 .credentials(auth.username(), auth.password(), domain)
192 .scopeToProject(project, domain)
193 .withConfig(config);
194
195 if (perspective != null) {
196 builder.perspective(getFacing(perspective));
197 }
198
199 return builder.authenticate();
200 } else {
201 log.warn("Unrecognized keystone version type");
202 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900203 }
Jian Li51b844c2018-05-31 10:59:03 +0900204 } catch (AuthenticationException e) {
205 log.error("Authentication failed due to {}", e.toString());
206 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900207 }
Jian Li1064e4f2018-05-29 16:16:53 +0900208 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900209
210 /**
211 * Extract the interface name with the supplied port.
212 *
213 * @param port port
214 * @return interface name
215 */
216 public static String getIntfNameFromPciAddress(Port port) {
Jian Li51b844c2018-05-31 10:59:03 +0900217
218 if (port.getProfile() == null) {
219 log.error("Port profile is not found");
220 return null;
221 }
222
Daniel Parkc4d06402018-05-28 15:57:37 +0900223 if (port.getProfile() != null && port.getProfile().get(PCISLOT) == null) {
224 log.error("Failed to retrieve the interface name because of no pci_slot information from the port");
225 return null;
226 }
Jian Li51b844c2018-05-31 10:59:03 +0900227
Daniel Parkc4d06402018-05-28 15:57:37 +0900228 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
229 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
230
231 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
232 .split(":")[2]
233 .split("\\.")[0];
234 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
235
236 String functionNumHex = port.getProfile().get(PCISLOT).toString()
237 .split(":")[2]
238 .split("\\.")[1];
239 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
240
241 String intfName;
242
243 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
244
245 if (vendorInfoForPort == null) {
246 log.error("Failed to retrieve the interface name because of no pci vendor information from the port");
247 return null;
248 }
Ray Milkey9dc57392018-06-08 08:52:31 -0700249 String portNamePrefix = portNamePrefixMap().get(vendorInfoForPort);
Jian Li51b844c2018-05-31 10:59:03 +0900250
Daniel Parkc4d06402018-05-28 15:57:37 +0900251 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
252 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
253 } else {
254 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
255 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
256 }
257
258 return intfName;
259 }
Jian Li51b844c2018-05-31 10:59:03 +0900260
261 /**
262 * Builds up and a complete endpoint URL from gateway node.
263 *
264 * @param node gateway node
265 * @return a complete endpoint URL
266 */
267 private static String buildEndpoint(OpenstackNode node) {
268
269 OpenstackAuth auth = node.authentication();
270
271 StringBuilder endpointSb = new StringBuilder();
272 endpointSb.append(auth.protocol().name().toLowerCase());
273 endpointSb.append("://");
274 endpointSb.append(node.endPoint());
275 endpointSb.append(":");
276 endpointSb.append(auth.port());
277 endpointSb.append("/");
278
279 // in case the version is v3, we need to append identity path into endpoint
280 if (auth.version().equals(KEYSTONE_V3)) {
281 endpointSb.append(IDENTITY_PATH);
282 }
283
284 endpointSb.append(auth.version());
285 return endpointSb.toString();
286 }
287
288 /**
289 * Obtains the SSL config without verifying the certification.
290 *
291 * @return SSL config
292 */
293 private static Config getSslConfig() {
294 // we bypass the SSL certification verification for now
295 // TODO: verify server side SSL using a given certification
296 Config config = Config.newConfig().withSSLVerificationDisabled();
297
298 TrustManager[] trustAllCerts = new TrustManager[]{
299 new X509TrustManager() {
300 public X509Certificate[] getAcceptedIssuers() {
301 return null;
302 }
303
304 public void checkClientTrusted(X509Certificate[] certs,
305 String authType) {
306 }
307
308 public void checkServerTrusted(X509Certificate[] certs,
309 String authType) {
310 }
311 }
312 };
313
314 HostnameVerifier allHostsValid = (hostname, session) -> true;
315
316 try {
317 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
318 sc.init(null, trustAllCerts,
319 new java.security.SecureRandom());
320 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
321 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
322
323 config.withSSLContext(sc);
324 } catch (Exception e) {
325 log.error("Failed to access OpenStack service due to {}", e.toString());
326 return null;
327 }
328
329 return config;
330 }
331
332 /**
333 * Obtains the facing object with given openstack perspective.
334 *
335 * @param perspective keystone perspective
336 * @return facing object
337 */
338 private static Facing getFacing(Perspective perspective) {
339
340 switch (perspective) {
341 case PUBLIC:
342 return Facing.PUBLIC;
343 case ADMIN:
344 return Facing.ADMIN;
345 case INTERNAL:
346 return Facing.INTERNAL;
347 default:
348 return null;
349 }
350 }
351
352 /**
353 * Obtains gateway instance by giving index number.
354 *
355 * @param gws a collection of gateway nodes
356 * @param index index number
357 * @return gateway instance
358 */
359 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
360 Map<String, OpenstackNode> hashMap = new HashMap<>();
361 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
362 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
363 Iterator<String> iteratorKey = treeMap.keySet().iterator();
364
365 int intIndex = 0;
366 OpenstackNode gw = null;
367 while (iteratorKey.hasNext()) {
368 String key = iteratorKey.next();
369
370 if (intIndex == index) {
371 gw = treeMap.get(key);
372 }
373 intIndex++;
374 }
375 return gw;
376 }
Jian Li091d8d22018-02-20 10:42:06 +0900377}