blob: fac184ef407fca4170ed3b32406366782b542678 [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";
58 private static final String IDENTITY_PATH = "identity/";
59 private static final String SSL_TYPE = "SSL";
60
Daniel Parkc4d06402018-05-28 15:57:37 +090061 /**
Jian Li51b844c2018-05-31 10:59:03 +090062 * Prevents object installation from external.
Daniel Parkc4d06402018-05-28 15:57:37 +090063 */
64 private OpenstackNodeUtil() {
65 }
66
67 /**
68 * Checks whether the controller has a connection with an OVSDB that resides
69 * inside the given openstack node.
70 *
71 * @param osNode openstack node
72 * @param ovsdbPort ovsdb port
73 * @param ovsdbController ovsdb controller
74 * @param deviceService device service
75 * @return true if the controller is connected to the OVSDB, false otherwise
76 */
77 public static boolean isOvsdbConnected(OpenstackNode osNode,
78 int ovsdbPort,
79 OvsdbController ovsdbController,
80 DeviceService deviceService) {
81 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
82 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
83 return deviceService.isAvailable(osNode.ovsdb()) &&
84 client != null &&
85 client.isConnected();
86 }
Jian Li51b844c2018-05-31 10:59:03 +090087
88 /**
89 * Obtains a connected openstack client.
90 *
91 * @param osNode openstack node
92 * @return a connected openstack client
93 */
94 public static OSClient getConnectedClient(OpenstackNode osNode) {
95 OpenstackAuth auth = osNode.authentication();
96 String endpoint = buildEndpoint(osNode);
97 Perspective perspective = auth.perspective();
98
99 Config config = getSslConfig();
100
101 try {
102 if (endpoint.contains(KEYSTONE_V2)) {
103 IOSClientBuilder.V2 builder = OSFactory.builderV2()
104 .endpoint(endpoint)
105 .tenantName(auth.project())
106 .credentials(auth.username(), auth.password())
107 .withConfig(config);
108
109 if (perspective != null) {
110 builder.perspective(getFacing(perspective));
111 }
112
113 return builder.authenticate();
114 } else if (endpoint.contains(KEYSTONE_V3)) {
115
116 Identifier project = Identifier.byName(auth.project());
117 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
118
119 IOSClientBuilder.V3 builder = OSFactory.builderV3()
120 .endpoint(endpoint)
121 .credentials(auth.username(), auth.password(), domain)
122 .scopeToProject(project, domain)
123 .withConfig(config);
124
125 if (perspective != null) {
126 builder.perspective(getFacing(perspective));
127 }
128
129 return builder.authenticate();
130 } else {
131 log.warn("Unrecognized keystone version type");
132 return null;
133 }
134 } catch (AuthenticationException e) {
135 log.error("Authentication failed due to {}", e.toString());
136 return null;
137 }
138 }
139
140 /**
Jian Li97482c12018-07-03 01:08:23 +0900141 * Gets Boolean property from the propertyName
142 * Return null if propertyName is not found.
143 *
144 * @param properties properties to be looked up
145 * @param propertyName the name of the property to look up
146 * @return value when the propertyName is defined or return null
147 */
148 public static Boolean getBooleanProperty(Dictionary<?, ?> properties,
149 String propertyName) {
150 Boolean value;
151 try {
152 String s = get(properties, propertyName);
153 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
154 } catch (ClassCastException e) {
155 value = null;
156 }
157 return value;
158 }
159
160 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900161 * Prints out the JSON string in pretty format.
162 *
163 * @param mapper Object mapper
164 * @param jsonString JSON string
165 * @return pretty formatted JSON string
166 */
167 public static String prettyJson(ObjectMapper mapper, String jsonString) {
168 try {
169 Object jsonObject = mapper.readValue(jsonString, Object.class);
170 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
171 } catch (IOException e) {
172 log.debug("Json string parsing exception caused by {}", e);
173 }
174 return null;
175 }
176
177 /**
Jian Li51b844c2018-05-31 10:59:03 +0900178 * Builds up and a complete endpoint URL from gateway node.
179 *
180 * @param node gateway node
181 * @return a complete endpoint URL
182 */
183 private static String buildEndpoint(OpenstackNode node) {
184
185 OpenstackAuth auth = node.authentication();
186
187 StringBuilder endpointSb = new StringBuilder();
188 endpointSb.append(auth.protocol().name().toLowerCase());
189 endpointSb.append("://");
190 endpointSb.append(node.endPoint());
191 endpointSb.append(":");
192 endpointSb.append(auth.port());
193 endpointSb.append("/");
194
195 // in case the version is v3, we need to append identity path into endpoint
196 if (auth.version().equals(KEYSTONE_V3)) {
197 endpointSb.append(IDENTITY_PATH);
198 }
199
200 endpointSb.append(auth.version());
201 return endpointSb.toString();
202 }
203
204 /**
205 * Obtains the SSL config without verifying the certification.
206 *
207 * @return SSL config
208 */
209 private static Config getSslConfig() {
210 // we bypass the SSL certification verification for now
211 // TODO: verify server side SSL using a given certification
212 Config config = Config.newConfig().withSSLVerificationDisabled();
213
214 TrustManager[] trustAllCerts = new TrustManager[]{
215 new X509TrustManager() {
216 public X509Certificate[] getAcceptedIssuers() {
217 return null;
218 }
219
220 public void checkClientTrusted(X509Certificate[] certs,
221 String authType) {
222 }
223
224 public void checkServerTrusted(X509Certificate[] certs,
225 String authType) {
226 }
227 }
228 };
229
230 HostnameVerifier allHostsValid = (hostname, session) -> true;
231
232 try {
233 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
234 sc.init(null, trustAllCerts,
235 new java.security.SecureRandom());
236 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
237 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
238
239 config.withSSLContext(sc);
240 } catch (Exception e) {
241 log.error("Failed to access OpenStack service due to {}", e.toString());
242 return null;
243 }
244
245 return config;
246 }
247
248 /**
249 * Obtains the facing object with given openstack perspective.
250 *
251 * @param perspective keystone perspective
252 * @return facing object
253 */
254 private static Facing getFacing(Perspective perspective) {
255
256 switch (perspective) {
257 case PUBLIC:
258 return Facing.PUBLIC;
259 case ADMIN:
260 return Facing.ADMIN;
261 case INTERNAL:
262 return Facing.INTERNAL;
263 default:
264 return null;
265 }
266 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900267}