blob: f19b8d54cc0584e5903508bc663e7278b37146d1 [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 Park5a6a7102018-09-06 23:58:33 +090020import com.google.common.collect.ImmutableMap;
21import org.onosproject.net.Device;
22import org.onosproject.net.behaviour.BridgeConfig;
23import org.onosproject.net.behaviour.BridgeName;
Daniel Parkc4d06402018-05-28 15:57:37 +090024import org.onosproject.net.device.DeviceService;
Daniel Park5a6a7102018-09-06 23:58:33 +090025import org.onosproject.openstacknode.api.DpdkInterface;
Jian Li51b844c2018-05-31 10:59:03 +090026import org.onosproject.openstacknode.api.OpenstackAuth;
27import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
Daniel Parkc4d06402018-05-28 15:57:37 +090028import org.onosproject.openstacknode.api.OpenstackNode;
29import org.onosproject.ovsdb.controller.OvsdbClientService;
30import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Park5a6a7102018-09-06 23:58:33 +090031import org.onosproject.ovsdb.controller.OvsdbInterface;
Daniel Parkc4d06402018-05-28 15:57:37 +090032import org.onosproject.ovsdb.controller.OvsdbNodeId;
Jian Li51b844c2018-05-31 10:59:03 +090033import org.openstack4j.api.OSClient;
34import org.openstack4j.api.client.IOSClientBuilder;
35import org.openstack4j.api.exceptions.AuthenticationException;
36import org.openstack4j.api.types.Facing;
37import org.openstack4j.core.transport.Config;
38import org.openstack4j.model.common.Identifier;
39import org.openstack4j.openstack.OSFactory;
Daniel Parkc4d06402018-05-28 15:57:37 +090040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Jian Li51b844c2018-05-31 10:59:03 +090043import javax.net.ssl.HostnameVerifier;
44import javax.net.ssl.HttpsURLConnection;
45import javax.net.ssl.SSLContext;
46import javax.net.ssl.TrustManager;
47import javax.net.ssl.X509TrustManager;
Jian Lif1efbe52018-07-17 23:20:16 +090048import java.io.IOException;
Jian Li51b844c2018-05-31 10:59:03 +090049import java.security.cert.X509Certificate;
Jian Li97482c12018-07-03 01:08:23 +090050import java.util.Dictionary;
Daniel Park5a6a7102018-09-06 23:58:33 +090051import java.util.Map;
Jian Li97482c12018-07-03 01:08:23 +090052
53import static org.onlab.util.Tools.get;
Jian Li51b844c2018-05-31 10:59:03 +090054
Daniel Parkc4d06402018-05-28 15:57:37 +090055/**
56 * An utility that used in openstack node app.
57 */
58public final class OpenstackNodeUtil {
Daniel Park95985382018-07-23 11:38:07 +090059 private static final Logger log = LoggerFactory.getLogger(OpenstackNodeUtil.class);
Daniel Parkc4d06402018-05-28 15:57:37 +090060
Jian Li51b844c2018-05-31 10:59:03 +090061 // keystone endpoint related variables
62 private static final String DOMAIN_DEFAULT = "default";
63 private static final String KEYSTONE_V2 = "v2.0";
64 private static final String KEYSTONE_V3 = "v3";
Jian Li51b844c2018-05-31 10:59:03 +090065 private static final String SSL_TYPE = "SSL";
66
Jian Lie3141542018-08-13 18:05:43 +090067 private static final int HEX_LENGTH = 16;
68 private static final String OF_PREFIX = "of:";
69 private static final String ZERO = "0";
70
Daniel Park5a6a7102018-09-06 23:58:33 +090071 private static final String DPDK_DEVARGS = "dpdk-devargs";
72
Daniel Parkc4d06402018-05-28 15:57:37 +090073 /**
Jian Li51b844c2018-05-31 10:59:03 +090074 * Prevents object installation from external.
Daniel Parkc4d06402018-05-28 15:57:37 +090075 */
76 private OpenstackNodeUtil() {
77 }
78
79 /**
80 * Checks whether the controller has a connection with an OVSDB that resides
81 * inside the given openstack node.
82 *
83 * @param osNode openstack node
84 * @param ovsdbPort ovsdb port
85 * @param ovsdbController ovsdb controller
86 * @param deviceService device service
87 * @return true if the controller is connected to the OVSDB, false otherwise
88 */
89 public static boolean isOvsdbConnected(OpenstackNode osNode,
90 int ovsdbPort,
91 OvsdbController ovsdbController,
92 DeviceService deviceService) {
Daniel Parke2658ba2018-08-24 22:33:29 +090093 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
Daniel Parkc4d06402018-05-28 15:57:37 +090094 return deviceService.isAvailable(osNode.ovsdb()) &&
95 client != null &&
96 client.isConnected();
97 }
Jian Li51b844c2018-05-31 10:59:03 +090098
99 /**
Daniel Parke2658ba2018-08-24 22:33:29 +0900100 * Gets the ovsdb client with supplied openstack node.
101 *
102 * @param osNode openstack node
103 * @param ovsdbPort ovsdb port
104 * @param ovsdbController ovsdb controller
105 * @return ovsdb client
106 */
107 public static OvsdbClientService getOvsdbClient(OpenstackNode osNode,
108 int ovsdbPort,
109 OvsdbController ovsdbController) {
110 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
111 return ovsdbController.getOvsdbClient(ovsdb);
112 }
113
114 /**
Jian Li51b844c2018-05-31 10:59:03 +0900115 * Obtains a connected openstack client.
116 *
117 * @param osNode openstack node
118 * @return a connected openstack client
119 */
120 public static OSClient getConnectedClient(OpenstackNode osNode) {
Jian Lic704b672018-09-04 18:52:53 +0900121 OpenstackAuth auth = osNode.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900122 String endpoint = buildEndpoint(osNode);
123 Perspective perspective = auth.perspective();
124
125 Config config = getSslConfig();
126
127 try {
128 if (endpoint.contains(KEYSTONE_V2)) {
129 IOSClientBuilder.V2 builder = OSFactory.builderV2()
130 .endpoint(endpoint)
131 .tenantName(auth.project())
132 .credentials(auth.username(), auth.password())
133 .withConfig(config);
134
135 if (perspective != null) {
136 builder.perspective(getFacing(perspective));
137 }
138
139 return builder.authenticate();
140 } else if (endpoint.contains(KEYSTONE_V3)) {
141
142 Identifier project = Identifier.byName(auth.project());
143 Identifier domain = Identifier.byName(DOMAIN_DEFAULT);
144
145 IOSClientBuilder.V3 builder = OSFactory.builderV3()
146 .endpoint(endpoint)
147 .credentials(auth.username(), auth.password(), domain)
148 .scopeToProject(project, domain)
149 .withConfig(config);
150
151 if (perspective != null) {
152 builder.perspective(getFacing(perspective));
153 }
154
155 return builder.authenticate();
156 } else {
157 log.warn("Unrecognized keystone version type");
158 return null;
159 }
160 } catch (AuthenticationException e) {
161 log.error("Authentication failed due to {}", e.toString());
162 return null;
163 }
164 }
165
166 /**
Jian Li97482c12018-07-03 01:08:23 +0900167 * Gets Boolean property from the propertyName
168 * Return null if propertyName is not found.
169 *
170 * @param properties properties to be looked up
171 * @param propertyName the name of the property to look up
172 * @return value when the propertyName is defined or return null
173 */
174 public static Boolean getBooleanProperty(Dictionary<?, ?> properties,
175 String propertyName) {
176 Boolean value;
177 try {
178 String s = get(properties, propertyName);
179 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
180 } catch (ClassCastException e) {
181 value = null;
182 }
183 return value;
184 }
185
186 /**
Jian Lif1efbe52018-07-17 23:20:16 +0900187 * Prints out the JSON string in pretty format.
188 *
189 * @param mapper Object mapper
190 * @param jsonString JSON string
191 * @return pretty formatted JSON string
192 */
193 public static String prettyJson(ObjectMapper mapper, String jsonString) {
194 try {
195 Object jsonObject = mapper.readValue(jsonString, Object.class);
196 return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
197 } catch (IOException e) {
198 log.debug("Json string parsing exception caused by {}", e);
199 }
200 return null;
201 }
202
203 /**
Jian Lie3141542018-08-13 18:05:43 +0900204 * Generates a DPID (of:0000000000000001) from an index value.
205 *
206 * @param index index value
207 * @return generated DPID
208 */
209 public static String genDpid(long index) {
210 if (index < 0) {
211 return null;
212 }
213
214 String hexStr = Long.toHexString(index);
215
216 StringBuilder zeroPadding = new StringBuilder();
217 for (int i = 0; i < HEX_LENGTH - hexStr.length(); i++) {
218 zeroPadding.append(ZERO);
219 }
220
221 return OF_PREFIX + zeroPadding.toString() + hexStr;
222 }
223
Daniel Park5a6a7102018-09-06 23:58:33 +0900224
225 /**
226 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
227 *
228 * @param osNode openstack node
229 * @param bridgeName bridge name
230 * @param intfName interface name
231 * @param deviceService device service
232 * @param addOrRemove add port is true, remove it otherwise
233 */
234 public static synchronized void addOrRemoveSystemInterface(OpenstackNode osNode,
235 String bridgeName,
236 String intfName,
237 DeviceService deviceService,
238 boolean addOrRemove) {
239
240
241 Device device = deviceService.getDevice(osNode.ovsdb());
242 if (device == null || !device.is(BridgeConfig.class)) {
243 log.info("device is null or this device if not ovsdb device");
244 return;
245 }
246 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
247
248 if (addOrRemove) {
249 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
250 } else {
251 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
252 }
253 }
254
255 /**
256 * Adds or removes a dpdk interface into a given openstack node.
257 *
258 * @param osNode openstack node
259 * @param dpdkInterface dpdk interface
260 * @param ovsdbPort ovsdb port
261 * @param ovsdbController ovsdb controller
262 * @param addOrRemove add port is true, remove it otherwise
263 */
264 public static synchronized void addOrRemoveDpdkInterface(OpenstackNode osNode,
265 DpdkInterface dpdkInterface,
266 int ovsdbPort,
267 OvsdbController ovsdbController,
268 boolean addOrRemove) {
269
270 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
271 if (client == null) {
272 log.info("Failed to get ovsdb client");
273 return;
274 }
275
276 if (addOrRemove) {
277 Map<String, String> options = ImmutableMap.of(DPDK_DEVARGS, dpdkInterface.pciAddress());
278
279 OvsdbInterface.Builder builder = OvsdbInterface.builder()
280 .name(dpdkInterface.intf())
281 .type(OvsdbInterface.Type.DPDK)
282 .mtu(dpdkInterface.mtu())
283 .options(options);
284
285
286 client.createInterface(dpdkInterface.deviceName(), builder.build());
287 } else {
288 client.dropInterface(dpdkInterface.intf());
289 }
290 }
291
Jian Lie3141542018-08-13 18:05:43 +0900292 /**
Jian Li51b844c2018-05-31 10:59:03 +0900293 * Builds up and a complete endpoint URL from gateway node.
294 *
295 * @param node gateway node
296 * @return a complete endpoint URL
297 */
298 private static String buildEndpoint(OpenstackNode node) {
299
Jian Lic704b672018-09-04 18:52:53 +0900300 OpenstackAuth auth = node.keystoneConfig().authentication();
Jian Li51b844c2018-05-31 10:59:03 +0900301
302 StringBuilder endpointSb = new StringBuilder();
303 endpointSb.append(auth.protocol().name().toLowerCase());
304 endpointSb.append("://");
Jian Lic704b672018-09-04 18:52:53 +0900305 endpointSb.append(node.keystoneConfig().endpoint());
Jian Li51b844c2018-05-31 10:59:03 +0900306 return endpointSb.toString();
307 }
308
309 /**
310 * Obtains the SSL config without verifying the certification.
311 *
312 * @return SSL config
313 */
314 private static Config getSslConfig() {
315 // we bypass the SSL certification verification for now
316 // TODO: verify server side SSL using a given certification
317 Config config = Config.newConfig().withSSLVerificationDisabled();
318
319 TrustManager[] trustAllCerts = new TrustManager[]{
320 new X509TrustManager() {
321 public X509Certificate[] getAcceptedIssuers() {
322 return null;
323 }
324
325 public void checkClientTrusted(X509Certificate[] certs,
326 String authType) {
327 }
328
329 public void checkServerTrusted(X509Certificate[] certs,
330 String authType) {
331 }
332 }
333 };
334
335 HostnameVerifier allHostsValid = (hostname, session) -> true;
336
337 try {
338 SSLContext sc = SSLContext.getInstance(SSL_TYPE);
339 sc.init(null, trustAllCerts,
340 new java.security.SecureRandom());
341 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
342 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
343
344 config.withSSLContext(sc);
345 } catch (Exception e) {
346 log.error("Failed to access OpenStack service due to {}", e.toString());
347 return null;
348 }
349
350 return config;
351 }
352
353 /**
354 * Obtains the facing object with given openstack perspective.
355 *
356 * @param perspective keystone perspective
357 * @return facing object
358 */
359 private static Facing getFacing(Perspective perspective) {
360
361 switch (perspective) {
362 case PUBLIC:
363 return Facing.PUBLIC;
364 case ADMIN:
365 return Facing.ADMIN;
366 case INTERNAL:
367 return Facing.INTERNAL;
368 default:
369 return null;
370 }
371 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900372}