blob: 58f3c331181ccd11f1c8c3aa495bd9753e4994da [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 Li51b844c2018-05-31 10:59:03 +090022import org.onosproject.openstacknode.api.OpenstackAuth;
23import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Jian Li1064e4f2018-05-29 16:16:53 +090024import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li51b844c2018-05-31 10:59:03 +090025import org.openstack4j.api.OSClient;
26import org.openstack4j.api.client.IOSClientBuilder;
27import org.openstack4j.api.exceptions.AuthenticationException;
28import org.openstack4j.api.types.Facing;
29import org.openstack4j.core.transport.Config;
Jian Li091d8d22018-02-20 10:42:06 +090030import org.openstack4j.core.transport.ObjectMapperSingleton;
31import org.openstack4j.model.ModelEntity;
Daniel Parkc4d06402018-05-28 15:57:37 +090032import org.openstack4j.model.network.Port;
Jian Li51b844c2018-05-31 10:59:03 +090033import org.openstack4j.model.common.Identifier;
34import org.openstack4j.openstack.OSFactory;
Jian Li091d8d22018-02-20 10:42:06 +090035import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
Jian Li51b844c2018-05-31 10:59:03 +090038import javax.net.ssl.HostnameVerifier;
39import javax.net.ssl.HttpsURLConnection;
40import javax.net.ssl.SSLContext;
41import javax.net.ssl.TrustManager;
42import javax.net.ssl.X509TrustManager;
Jian Li091d8d22018-02-20 10:42:06 +090043import java.io.InputStream;
Jian Li51b844c2018-05-31 10:59:03 +090044import java.security.cert.X509Certificate;
Jian Li1064e4f2018-05-29 16:16:53 +090045import java.util.HashMap;
46import java.util.Iterator;
47import java.util.Map;
48import java.util.Set;
49import java.util.TreeMap;
Jian Li091d8d22018-02-20 10:42:06 +090050
51import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
Daniel Parkc4d06402018-05-28 15:57:37 +090052import static org.onosproject.openstacknetworking.api.Constants.PCISLOT;
53import static org.onosproject.openstacknetworking.api.Constants.PCI_VENDOR_INFO;
54import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_MAP;
Jian Li091d8d22018-02-20 10:42:06 +090055
56/**
57 * An utility that used in openstack networking app.
58 */
Jian Lidea0fdb2018-04-02 19:02:48 +090059public final class OpenstackNetworkingUtil {
Jian Li091d8d22018-02-20 10:42:06 +090060
Jian Lidea0fdb2018-04-02 19:02:48 +090061 protected static final Logger log = LoggerFactory.getLogger(OpenstackNetworkingUtil.class);
Jian Li091d8d22018-02-20 10:42:06 +090062
Daniel Parkc4d06402018-05-28 15:57:37 +090063 private static final int HEX_RADIX = 16;
Jian Li51b844c2018-05-31 10:59:03 +090064 private static final String ZERO_FUNCTION_NUMBER = "0";
Daniel Parkc4d06402018-05-28 15:57:37 +090065 private static final String PREFIX_DEVICE_NUMBER = "s";
66 private static final String PREFIX_FUNCTION_NUMBER = "f";
67
Jian Li51b844c2018-05-31 10:59:03 +090068 // keystone endpoint related variables
69 private static final String DOMAIN_DEFAULT = "default";
70 private static final String KEYSTONE_V2 = "v2.0";
71 private static final String KEYSTONE_V3 = "v3";
72 private static final String IDENTITY_PATH = "identity/";
73 private static final String SSL_TYPE = "SSL";
74
Jian Li091d8d22018-02-20 10:42:06 +090075 /**
76 * Prevents object instantiation from external.
77 */
Jian Lidea0fdb2018-04-02 19:02:48 +090078 private OpenstackNetworkingUtil() {
Jian Li091d8d22018-02-20 10:42:06 +090079 }
80
81 /**
82 * Interprets JSON string to corresponding openstack model entity object.
83 *
84 * @param input JSON string
85 * @param entityClazz openstack model entity class
86 * @return openstack model entity object
87 */
88 public static ModelEntity jsonToModelEntity(InputStream input, Class entityClazz) {
89 ObjectMapper mapper = new ObjectMapper();
90 try {
91 JsonNode jsonTree = mapper.enable(INDENT_OUTPUT).readTree(input);
92 log.trace(new ObjectMapper().writeValueAsString(jsonTree));
93 return ObjectMapperSingleton.getContext(entityClazz)
94 .readerFor(entityClazz)
95 .readValue(jsonTree);
96 } catch (Exception e) {
97 throw new IllegalArgumentException();
98 }
99 }
Jian Lieb9f77d2018-02-20 11:25:45 +0900100
101 /**
102 * Converts openstack model entity object into JSON object.
103 *
104 * @param entity openstack model entity object
105 * @param entityClazz openstack model entity class
106 * @return JSON object
107 */
108 public static ObjectNode modelEntityToJson(ModelEntity entity, Class entityClazz) {
109 ObjectMapper mapper = new ObjectMapper();
110 try {
111 String strModelEntity = ObjectMapperSingleton.getContext(entityClazz)
112 .writerFor(entityClazz)
113 .writeValueAsString(entity);
114 log.trace(strModelEntity);
115 return (ObjectNode) mapper.readTree(strModelEntity.getBytes());
116 } catch (Exception e) {
117 throw new IllegalStateException();
118 }
119 }
Jian Li1064e4f2018-05-29 16:16:53 +0900120
121 /**
122 * Obtains the gateway node by device in compute node. Note that the gateway
123 * node is determined by device's device identifier.
124 *
125 * @param gws a collection of gateway nodes
126 * @param deviceId device identifier
127 * @return a gateway node
128 */
129 public static OpenstackNode getGwByComputeDevId(Set<OpenstackNode> gws, DeviceId deviceId) {
130 int numOfGw = gws.size();
131
132 if (numOfGw == 0) {
133 return null;
134 }
135
136 int gwIndex = Math.abs(deviceId.hashCode()) % numOfGw;
137
138 return getGwByIndex(gws, gwIndex);
139 }
140
Jian Li51b844c2018-05-31 10:59:03 +0900141 /**
142 * Obtains a connected openstack client.
143 *
144 * @param osNode openstack node
145 * @return a connected openstack client
146 */
147 public static OSClient getConnectedClient(OpenstackNode osNode) {
148 OpenstackAuth auth = osNode.authentication();
149 String endpoint = buildEndpoint(osNode);
150 Perspective perspective = auth.perspective();
Jian Li1064e4f2018-05-29 16:16:53 +0900151
Jian Li51b844c2018-05-31 10:59:03 +0900152 Config config = getSslConfig();
Jian Li1064e4f2018-05-29 16:16:53 +0900153
Jian Li51b844c2018-05-31 10:59:03 +0900154 try {
155 if (endpoint.contains(KEYSTONE_V2)) {
156 IOSClientBuilder.V2 builder = OSFactory.builderV2()
157 .endpoint(endpoint)
158 .tenantName(auth.project())
159 .credentials(auth.username(), auth.password())
160 .withConfig(config);
161
162 if (perspective != null) {
163 builder.perspective(getFacing(perspective));
164 }
165
166 return builder.authenticate();
167 } else if (endpoint.contains(KEYSTONE_V3)) {
168
169 Identifier project = Identifier.byName(auth.project());
170 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
171
172 IOSClientBuilder.V3 builder = OSFactory.builderV3()
173 .endpoint(endpoint)
174 .credentials(auth.username(), auth.password(), domain)
175 .scopeToProject(project, domain)
176 .withConfig(config);
177
178 if (perspective != null) {
179 builder.perspective(getFacing(perspective));
180 }
181
182 return builder.authenticate();
183 } else {
184 log.warn("Unrecognized keystone version type");
185 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900186 }
Jian Li51b844c2018-05-31 10:59:03 +0900187 } catch (AuthenticationException e) {
188 log.error("Authentication failed due to {}", e.toString());
189 return null;
Jian Li1064e4f2018-05-29 16:16:53 +0900190 }
Jian Li1064e4f2018-05-29 16:16:53 +0900191 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900192
193 /**
194 * Extract the interface name with the supplied port.
195 *
196 * @param port port
197 * @return interface name
198 */
199 public static String getIntfNameFromPciAddress(Port port) {
Jian Li51b844c2018-05-31 10:59:03 +0900200
201 if (port.getProfile() == null) {
202 log.error("Port profile is not found");
203 return null;
204 }
205
Daniel Parkc4d06402018-05-28 15:57:37 +0900206 if (port.getProfile() != null && port.getProfile().get(PCISLOT) == null) {
207 log.error("Failed to retrieve the interface name because of no pci_slot information from the port");
208 return null;
209 }
Jian Li51b844c2018-05-31 10:59:03 +0900210
Daniel Parkc4d06402018-05-28 15:57:37 +0900211 String busNumHex = port.getProfile().get(PCISLOT).toString().split(":")[1];
212 String busNumDecimal = String.valueOf(Integer.parseInt(busNumHex, HEX_RADIX));
213
214 String deviceNumHex = port.getProfile().get(PCISLOT).toString()
215 .split(":")[2]
216 .split("\\.")[0];
217 String deviceNumDecimal = String.valueOf(Integer.parseInt(deviceNumHex, HEX_RADIX));
218
219 String functionNumHex = port.getProfile().get(PCISLOT).toString()
220 .split(":")[2]
221 .split("\\.")[1];
222 String functionNumDecimal = String.valueOf(Integer.parseInt(functionNumHex, HEX_RADIX));
223
224 String intfName;
225
226 String vendorInfoForPort = String.valueOf(port.getProfile().get(PCI_VENDOR_INFO));
227
228 if (vendorInfoForPort == null) {
229 log.error("Failed to retrieve the interface name because of no pci vendor information from the port");
230 return null;
231 }
232 String portNamePrefix = PORT_NAME_PREFIX_MAP.get(vendorInfoForPort);
Jian Li51b844c2018-05-31 10:59:03 +0900233
Daniel Parkc4d06402018-05-28 15:57:37 +0900234 if (functionNumDecimal.equals(ZERO_FUNCTION_NUMBER)) {
235 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal;
236 } else {
237 intfName = portNamePrefix + busNumDecimal + PREFIX_DEVICE_NUMBER + deviceNumDecimal
238 + PREFIX_FUNCTION_NUMBER + functionNumDecimal;
239 }
240
241 return intfName;
242 }
Jian Li51b844c2018-05-31 10:59:03 +0900243
244 /**
245 * Builds up and a complete endpoint URL from gateway node.
246 *
247 * @param node gateway node
248 * @return a complete endpoint URL
249 */
250 private static String buildEndpoint(OpenstackNode node) {
251
252 OpenstackAuth auth = node.authentication();
253
254 StringBuilder endpointSb = new StringBuilder();
255 endpointSb.append(auth.protocol().name().toLowerCase());
256 endpointSb.append("://");
257 endpointSb.append(node.endPoint());
258 endpointSb.append(":");
259 endpointSb.append(auth.port());
260 endpointSb.append("/");
261
262 // in case the version is v3, we need to append identity path into endpoint
263 if (auth.version().equals(KEYSTONE_V3)) {
264 endpointSb.append(IDENTITY_PATH);
265 }
266
267 endpointSb.append(auth.version());
268 return endpointSb.toString();
269 }
270
271 /**
272 * Obtains the SSL config without verifying the certification.
273 *
274 * @return SSL config
275 */
276 private static Config getSslConfig() {
277 // we bypass the SSL certification verification for now
278 // TODO: verify server side SSL using a given certification
279 Config config = Config.newConfig().withSSLVerificationDisabled();
280
281 TrustManager[] trustAllCerts = new TrustManager[]{
282 new X509TrustManager() {
283 public X509Certificate[] getAcceptedIssuers() {
284 return null;
285 }
286
287 public void checkClientTrusted(X509Certificate[] certs,
288 String authType) {
289 }
290
291 public void checkServerTrusted(X509Certificate[] certs,
292 String authType) {
293 }
294 }
295 };
296
297 HostnameVerifier allHostsValid = (hostname, session) -> true;
298
299 try {
300 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
301 sc.init(null, trustAllCerts,
302 new java.security.SecureRandom());
303 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
304 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
305
306 config.withSSLContext(sc);
307 } catch (Exception e) {
308 log.error("Failed to access OpenStack service due to {}", e.toString());
309 return null;
310 }
311
312 return config;
313 }
314
315 /**
316 * Obtains the facing object with given openstack perspective.
317 *
318 * @param perspective keystone perspective
319 * @return facing object
320 */
321 private static Facing getFacing(Perspective perspective) {
322
323 switch (perspective) {
324 case PUBLIC:
325 return Facing.PUBLIC;
326 case ADMIN:
327 return Facing.ADMIN;
328 case INTERNAL:
329 return Facing.INTERNAL;
330 default:
331 return null;
332 }
333 }
334
335 /**
336 * Obtains gateway instance by giving index number.
337 *
338 * @param gws a collection of gateway nodes
339 * @param index index number
340 * @return gateway instance
341 */
342 private static OpenstackNode getGwByIndex(Set<OpenstackNode> gws, int index) {
343 Map<String, OpenstackNode> hashMap = new HashMap<>();
344 gws.forEach(gw -> hashMap.put(gw.hostname(), gw));
345 TreeMap<String, OpenstackNode> treeMap = new TreeMap<>(hashMap);
346 Iterator<String> iteratorKey = treeMap.keySet().iterator();
347
348 int intIndex = 0;
349 OpenstackNode gw = null;
350 while (iteratorKey.hasNext()) {
351 String key = iteratorKey.next();
352
353 if (intIndex == index) {
354 gw = treeMap.get(key);
355 }
356 intIndex++;
357 }
358 return gw;
359 }
Jian Li091d8d22018-02-20 10:42:06 +0900360}