blob: 6012c6f1f6ed25bc8e0b98f54c7ad595b0305ad5 [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 Li97482c12018-07-03 01:08:23 +090018import com.google.common.base.Strings;
Daniel Parkc4d06402018-05-28 15:57:37 +090019import org.onosproject.net.device.DeviceService;
Jian Li51b844c2018-05-31 10:59:03 +090020import org.onosproject.openstacknode.api.OpenstackAuth;
21import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Daniel Parkc4d06402018-05-28 15:57:37 +090022import org.onosproject.openstacknode.api.OpenstackNode;
23import org.onosproject.ovsdb.controller.OvsdbClientService;
24import org.onosproject.ovsdb.controller.OvsdbController;
25import org.onosproject.ovsdb.controller.OvsdbNodeId;
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;
31import org.openstack4j.model.common.Identifier;
32import org.openstack4j.openstack.OSFactory;
Daniel Parkc4d06402018-05-28 15:57:37 +090033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Jian Li51b844c2018-05-31 10:59:03 +090036import javax.net.ssl.HostnameVerifier;
37import javax.net.ssl.HttpsURLConnection;
38import javax.net.ssl.SSLContext;
39import javax.net.ssl.TrustManager;
40import javax.net.ssl.X509TrustManager;
41import java.security.cert.X509Certificate;
Jian Li97482c12018-07-03 01:08:23 +090042import java.util.Dictionary;
43
44import static org.onlab.util.Tools.get;
Jian Li51b844c2018-05-31 10:59:03 +090045
Daniel Parkc4d06402018-05-28 15:57:37 +090046/**
47 * An utility that used in openstack node app.
48 */
49public final class OpenstackNodeUtil {
50 protected static final Logger log = LoggerFactory.getLogger(OpenstackNodeUtil.class);
51
Jian Li51b844c2018-05-31 10:59:03 +090052 // keystone endpoint related variables
53 private static final String DOMAIN_DEFAULT = "default";
54 private static final String KEYSTONE_V2 = "v2.0";
55 private static final String KEYSTONE_V3 = "v3";
56 private static final String IDENTITY_PATH = "identity/";
57 private static final String SSL_TYPE = "SSL";
58
Daniel Parkc4d06402018-05-28 15:57:37 +090059 /**
Jian Li51b844c2018-05-31 10:59:03 +090060 * Prevents object installation from external.
Daniel Parkc4d06402018-05-28 15:57:37 +090061 */
62 private OpenstackNodeUtil() {
63 }
64
65 /**
66 * Checks whether the controller has a connection with an OVSDB that resides
67 * inside the given openstack node.
68 *
69 * @param osNode openstack node
70 * @param ovsdbPort ovsdb port
71 * @param ovsdbController ovsdb controller
72 * @param deviceService device service
73 * @return true if the controller is connected to the OVSDB, false otherwise
74 */
75 public static boolean isOvsdbConnected(OpenstackNode osNode,
76 int ovsdbPort,
77 OvsdbController ovsdbController,
78 DeviceService deviceService) {
79 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
80 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
81 return deviceService.isAvailable(osNode.ovsdb()) &&
82 client != null &&
83 client.isConnected();
84 }
Jian Li51b844c2018-05-31 10:59:03 +090085
86 /**
87 * Obtains a connected openstack client.
88 *
89 * @param osNode openstack node
90 * @return a connected openstack client
91 */
92 public static OSClient getConnectedClient(OpenstackNode osNode) {
93 OpenstackAuth auth = osNode.authentication();
94 String endpoint = buildEndpoint(osNode);
95 Perspective perspective = auth.perspective();
96
97 Config config = getSslConfig();
98
99 try {
100 if (endpoint.contains(KEYSTONE_V2)) {
101 IOSClientBuilder.V2 builder = OSFactory.builderV2()
102 .endpoint(endpoint)
103 .tenantName(auth.project())
104 .credentials(auth.username(), auth.password())
105 .withConfig(config);
106
107 if (perspective != null) {
108 builder.perspective(getFacing(perspective));
109 }
110
111 return builder.authenticate();
112 } else if (endpoint.contains(KEYSTONE_V3)) {
113
114 Identifier project = Identifier.byName(auth.project());
115 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
116
117 IOSClientBuilder.V3 builder = OSFactory.builderV3()
118 .endpoint(endpoint)
119 .credentials(auth.username(), auth.password(), domain)
120 .scopeToProject(project, domain)
121 .withConfig(config);
122
123 if (perspective != null) {
124 builder.perspective(getFacing(perspective));
125 }
126
127 return builder.authenticate();
128 } else {
129 log.warn("Unrecognized keystone version type");
130 return null;
131 }
132 } catch (AuthenticationException e) {
133 log.error("Authentication failed due to {}", e.toString());
134 return null;
135 }
136 }
137
138 /**
Jian Li97482c12018-07-03 01:08:23 +0900139 * Gets Boolean property from the propertyName
140 * Return null if propertyName is not found.
141 *
142 * @param properties properties to be looked up
143 * @param propertyName the name of the property to look up
144 * @return value when the propertyName is defined or return null
145 */
146 public static Boolean getBooleanProperty(Dictionary<?, ?> properties,
147 String propertyName) {
148 Boolean value;
149 try {
150 String s = get(properties, propertyName);
151 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
152 } catch (ClassCastException e) {
153 value = null;
154 }
155 return value;
156 }
157
158 /**
Jian Li51b844c2018-05-31 10:59:03 +0900159 * Builds up and a complete endpoint URL from gateway node.
160 *
161 * @param node gateway node
162 * @return a complete endpoint URL
163 */
164 private static String buildEndpoint(OpenstackNode node) {
165
166 OpenstackAuth auth = node.authentication();
167
168 StringBuilder endpointSb = new StringBuilder();
169 endpointSb.append(auth.protocol().name().toLowerCase());
170 endpointSb.append("://");
171 endpointSb.append(node.endPoint());
172 endpointSb.append(":");
173 endpointSb.append(auth.port());
174 endpointSb.append("/");
175
176 // in case the version is v3, we need to append identity path into endpoint
177 if (auth.version().equals(KEYSTONE_V3)) {
178 endpointSb.append(IDENTITY_PATH);
179 }
180
181 endpointSb.append(auth.version());
182 return endpointSb.toString();
183 }
184
185 /**
186 * Obtains the SSL config without verifying the certification.
187 *
188 * @return SSL config
189 */
190 private static Config getSslConfig() {
191 // we bypass the SSL certification verification for now
192 // TODO: verify server side SSL using a given certification
193 Config config = Config.newConfig().withSSLVerificationDisabled();
194
195 TrustManager[] trustAllCerts = new TrustManager[]{
196 new X509TrustManager() {
197 public X509Certificate[] getAcceptedIssuers() {
198 return null;
199 }
200
201 public void checkClientTrusted(X509Certificate[] certs,
202 String authType) {
203 }
204
205 public void checkServerTrusted(X509Certificate[] certs,
206 String authType) {
207 }
208 }
209 };
210
211 HostnameVerifier allHostsValid = (hostname, session) -> true;
212
213 try {
214 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
215 sc.init(null, trustAllCerts,
216 new java.security.SecureRandom());
217 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
218 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
219
220 config.withSSLContext(sc);
221 } catch (Exception e) {
222 log.error("Failed to access OpenStack service due to {}", e.toString());
223 return null;
224 }
225
226 return config;
227 }
228
229 /**
230 * Obtains the facing object with given openstack perspective.
231 *
232 * @param perspective keystone perspective
233 * @return facing object
234 */
235 private static Facing getFacing(Perspective perspective) {
236
237 switch (perspective) {
238 case PUBLIC:
239 return Facing.PUBLIC;
240 case ADMIN:
241 return Facing.ADMIN;
242 case INTERNAL:
243 return Facing.INTERNAL;
244 default:
245 return null;
246 }
247 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900248}