blob: 311eba4c6404ba4588ab123f0e3a6bfde0911e41 [file] [log] [blame]
Daniel Parkc4d06402018-05-28 15:57:37 +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.openstacknode.util;
17
Jian Lif1efbe52018-07-17 23:20:16 +090018import com.fasterxml.jackson.databind.ObjectMapper;
Jian Li97482c12018-07-03 01:08:23 +090019import com.google.common.base.Strings;
Daniel Parkc4d06402018-05-28 15:57:37 +090020import org.onosproject.net.device.DeviceService;
Jian Li51b844c2018-05-31 10:59:03 +090021import org.onosproject.openstacknode.api.OpenstackAuth;
22import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Daniel Parkc4d06402018-05-28 15:57:37 +090023import org.onosproject.openstacknode.api.OpenstackNode;
24import org.onosproject.ovsdb.controller.OvsdbClientService;
25import org.onosproject.ovsdb.controller.OvsdbController;
26import org.onosproject.ovsdb.controller.OvsdbNodeId;
Jian Li51b844c2018-05-31 10:59:03 +090027import org.openstack4j.api.OSClient;
28import org.openstack4j.api.client.IOSClientBuilder;
29import org.openstack4j.api.exceptions.AuthenticationException;
30import org.openstack4j.api.types.Facing;
31import org.openstack4j.core.transport.Config;
32import org.openstack4j.model.common.Identifier;
33import org.openstack4j.openstack.OSFactory;
Daniel Parkc4d06402018-05-28 15:57:37 +090034import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
Jian Li51b844c2018-05-31 10:59:03 +090037import javax.net.ssl.HostnameVerifier;
38import javax.net.ssl.HttpsURLConnection;
39import javax.net.ssl.SSLContext;
40import javax.net.ssl.TrustManager;
41import javax.net.ssl.X509TrustManager;
Jian Lif1efbe52018-07-17 23:20:16 +090042import java.io.IOException;
Jian Li51b844c2018-05-31 10:59:03 +090043import java.security.cert.X509Certificate;
Jian Li97482c12018-07-03 01:08:23 +090044import java.util.Dictionary;
45
46import static org.onlab.util.Tools.get;
Jian Li51b844c2018-05-31 10:59:03 +090047
Daniel Parkc4d06402018-05-28 15:57:37 +090048/**
49 * An utility that used in openstack node app.
50 */
51public final class OpenstackNodeUtil {
Daniel Park95985382018-07-23 11:38:07 +090052 private static final Logger log = LoggerFactory.getLogger(OpenstackNodeUtil.class);
Daniel Parkc4d06402018-05-28 15:57:37 +090053
Jian Li51b844c2018-05-31 10:59:03 +090054 // keystone endpoint related variables
55 private static final String DOMAIN_DEFAULT = "default";
56 private static final String KEYSTONE_V2 = "v2.0";
57 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +090058 private static final String SSL_TYPE = "SSL";
59
Jian Lie3141542018-08-13 18:05:43 +090060 private static final int HEX_LENGTH = 16;
61 private static final String OF_PREFIX = "of:";
62 private static final String ZERO = "0";
63
Daniel Parkc4d06402018-05-28 15:57:37 +090064 /**
Jian Li51b844c2018-05-31 10:59:03 +090065 * Prevents object installation from external.
Daniel Parkc4d06402018-05-28 15:57:37 +090066 */
67 private OpenstackNodeUtil() {
68 }
69
70 /**
71 * Checks whether the controller has a connection with an OVSDB that resides
72 * inside the given openstack node.
73 *
74 * @param osNode openstack node
75 * @param ovsdbPort ovsdb port
76 * @param ovsdbController ovsdb controller
77 * @param deviceService device service
78 * @return true if the controller is connected to the OVSDB, false otherwise
79 */
80 public static boolean isOvsdbConnected(OpenstackNode osNode,
81 int ovsdbPort,
82 OvsdbController ovsdbController,
83 DeviceService deviceService) {
Daniel Parke2658ba2018-08-24 22:33:29 +090084 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
Daniel Parkc4d06402018-05-28 15:57:37 +090085 return deviceService.isAvailable(osNode.ovsdb()) &&
86 client != null &&
87 client.isConnected();
88 }
Jian Li51b844c2018-05-31 10:59:03 +090089
90 /**
Daniel Parke2658ba2018-08-24 22:33:29 +090091 * Gets the ovsdb client with supplied openstack node.
92 *
93 * @param osNode openstack node
94 * @param ovsdbPort ovsdb port
95 * @param ovsdbController ovsdb controller
96 * @return ovsdb client
97 */
98 public static OvsdbClientService getOvsdbClient(OpenstackNode osNode,
99 int ovsdbPort,
100 OvsdbController ovsdbController) {
101 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
102 return ovsdbController.getOvsdbClient(ovsdb);
103 }
104
105 /**
Jian Li51b844c2018-05-31 10:59:03 +0900106 * Obtains a connected openstack client.
107 *
108 * @param osNode openstack node
109 * @return a connected openstack client
110 */
111 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900112 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900113 String endpoint = buildEndpoint(osNode);
114 Perspective perspective = auth.perspective();
115
116 Config config = getSslConfig();
117
118 try {
119 if (endpoint.contains(KEYSTONE_V2)) {
120 IOSClientBuilder.V2 builder = OSFactory.builderV2()
121 .endpoint(endpoint)
122 .tenantName(auth.project())
123 .credentials(auth.username(), auth.password())
124 .withConfig(config);
125
126 if (perspective != null) {
127 builder.perspective(getFacing(perspective));
128 }
129
130 return builder.authenticate();
131 } else if (endpoint.contains(KEYSTONE_V3)) {
132
133 Identifier project = Identifier.byName(auth.project());
134 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
135
136 IOSClientBuilder.V3 builder = OSFactory.builderV3()
137 .endpoint(endpoint)
138 .credentials(auth.username(), auth.password(), domain)
139 .scopeToProject(project, domain)
140 .withConfig(config);
141
142 if (perspective != null) {
143 builder.perspective(getFacing(perspective));
144 }
145
146 return builder.authenticate();
147 } else {
148 log.warn("Unrecognized keystone version type");
149 return null;
150 }
151 } catch (AuthenticationException e) {
152 log.error("Authentication failed due to {}", e.toString());
153 return null;
154 }
155 }
156
157 /**
Jian Li97482c12018-07-03 01:08:23 +0900158 * Gets Boolean property from the propertyName
159 * Return null if propertyName is not found.
160 *
161 * @param properties properties to be looked up
162 * @param propertyName the name of the property to look up
163 * @return value when the propertyName is defined or return null
164 */
165 public static Boolean getBooleanProperty(Dictionary<?, ?> properties,
166 String propertyName) {
167 Boolean value;
168 try {
169 String s = get(properties, propertyName);
170 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
171 } catch (ClassCastException e) {
172 value = null;
173 }
174 return value;
175 }
176
177 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900178 * Prints out the JSON string in pretty format.
179 *
180 * @param mapper Object mapper
181 * @param jsonString JSON string
182 * @return pretty formatted JSON string
183 */
184 public static String prettyJson(ObjectMapper mapper, String jsonString) {
185 try {
186 Object jsonObject = mapper.readValue(jsonString, Object.class);
187 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
188 } catch (IOException e) {
189 log.debug("Json string parsing exception caused by {}", e);
190 }
191 return null;
192 }
193
194 /**
Jian Lie3141542018-08-13 18:05:43 +0900195 * Generates a DPID (of:0000000000000001) from an index value.
196 *
197 * @param index index value
198 * @return generated DPID
199 */
200 public static String genDpid(long index) {
201 if (index < 0) {
202 return null;
203 }
204
205 String hexStr = Long.toHexString(index);
206
207 StringBuilder zeroPadding = new StringBuilder();
208 for (int i = 0; i < HEX_LENGTH - hexStr.length(); i++) {
209 zeroPadding.append(ZERO);
210 }
211
212 return OF_PREFIX + zeroPadding.toString() + hexStr;
213 }
214
215 /**
Jian Li51b844c2018-05-31 10:59:03 +0900216 * Builds up and a complete endpoint URL from gateway node.
217 *
218 * @param node gateway node
219 * @return a complete endpoint URL
220 */
221 private static String buildEndpoint(OpenstackNode node) {
222
Jian Lic704b672018-09-04 18:52:53 +0900223 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900224
225 StringBuilder endpointSb = new StringBuilder();
226 endpointSb.append(auth.protocol().name().toLowerCase());
227 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +0900228 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +0900229 return endpointSb.toString();
230 }
231
232 /**
233 * Obtains the SSL config without verifying the certification.
234 *
235 * @return SSL config
236 */
237 private static Config getSslConfig() {
238 // we bypass the SSL certification verification for now
239 // TODO: verify server side SSL using a given certification
240 Config config = Config.newConfig().withSSLVerificationDisabled();
241
242 TrustManager[] trustAllCerts = new TrustManager[]{
243 new X509TrustManager() {
244 public X509Certificate[] getAcceptedIssuers() {
245 return null;
246 }
247
248 public void checkClientTrusted(X509Certificate[] certs,
249 String authType) {
250 }
251
252 public void checkServerTrusted(X509Certificate[] certs,
253 String authType) {
254 }
255 }
256 };
257
258 HostnameVerifier allHostsValid = (hostname, session) -> true;
259
260 try {
261 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
262 sc.init(null, trustAllCerts,
263 new java.security.SecureRandom());
264 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
265 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
266
267 config.withSSLContext(sc);
268 } catch (Exception e) {
269 log.error("Failed to access OpenStack service due to {}", e.toString());
270 return null;
271 }
272
273 return config;
274 }
275
276 /**
277 * Obtains the facing object with given openstack perspective.
278 *
279 * @param perspective keystone perspective
280 * @return facing object
281 */
282 private static Facing getFacing(Perspective perspective) {
283
284 switch (perspective) {
285 case PUBLIC:
286 return Facing.PUBLIC;
287 case ADMIN:
288 return Facing.ADMIN;
289 case INTERNAL:
290 return Facing.INTERNAL;
291 default:
292 return null;
293 }
294 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900295}