blob: b1e57bed2d214a92344d25e71ad7836576f4542f [file] [log] [blame]
Hyunsun Moonc7219222017-03-27 11:05:59 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moonc7219222017-03-27 11:05:59 +09003 *
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.cli;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.google.common.base.Strings;
Hyunsun Moonc7219222017-03-27 11:05:59 +090020import org.apache.karaf.shell.commands.Command;
21import org.onosproject.cli.AbstractShellCommand;
22import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Hyunsun Moonc7219222017-03-27 11:05:59 +090023import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Hyunsun Moonae51e732017-04-25 17:46:21 +090024import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
Jian Li39057872018-04-16 11:33:12 +090025import org.onosproject.openstacknode.api.OpenstackAuth;
26import org.onosproject.openstacknode.api.OpenstackNode;
27import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moonc7219222017-03-27 11:05:59 +090028import org.openstack4j.api.OSClient;
Jian Li8cbc3b92018-04-11 12:21:25 +090029import org.openstack4j.api.client.IOSClientBuilder;
Hyunsun Moonc7219222017-03-27 11:05:59 +090030import org.openstack4j.api.exceptions.AuthenticationException;
Jian Lib43b0672018-04-05 21:48:51 +090031import org.openstack4j.api.types.Facing;
Jian Li1a9e71c2018-04-03 19:00:29 +090032import org.openstack4j.core.transport.Config;
sanghoa0ef13b2018-03-09 13:56:39 +090033import org.openstack4j.model.common.Identifier;
Hyunsun Moonc7219222017-03-27 11:05:59 +090034import org.openstack4j.model.network.IP;
35import org.openstack4j.model.network.NetFloatingIP;
36import org.openstack4j.model.network.Network;
37import org.openstack4j.model.network.Port;
38import org.openstack4j.model.network.Router;
39import org.openstack4j.model.network.RouterInterface;
40import org.openstack4j.model.network.Subnet;
41import org.openstack4j.openstack.OSFactory;
42import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
43
Jian Li1a9e71c2018-04-03 19:00:29 +090044import javax.net.ssl.HostnameVerifier;
45import javax.net.ssl.HttpsURLConnection;
46import javax.net.ssl.SSLContext;
47import javax.net.ssl.TrustManager;
Jian Li39057872018-04-16 11:33:12 +090048import javax.net.ssl.X509TrustManager;
Hyunsun Moonc7219222017-03-27 11:05:59 +090049import java.io.IOException;
Jian Li39057872018-04-16 11:33:12 +090050import java.security.cert.X509Certificate;
Hyunsun Moonc7219222017-03-27 11:05:59 +090051import java.util.List;
52import java.util.Objects;
Jian Li39057872018-04-16 11:33:12 +090053import java.util.Optional;
Hyunsun Moonc7219222017-03-27 11:05:59 +090054import java.util.stream.Collectors;
55
Jian Li39057872018-04-16 11:33:12 +090056import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moonc7219222017-03-27 11:05:59 +090057import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
58
59/**
60 * Synchronizes OpenStack network states.
61 */
62@Command(scope = "onos", name = "openstack-sync-states",
63 description = "Synchronizes all OpenStack network states")
64public class OpenstackSyncStateCommand extends AbstractShellCommand {
65
sanghoa0ef13b2018-03-09 13:56:39 +090066 private static final String DOMAIN_DEFUALT = "default";
67
Hyunsun Moonae51e732017-04-25 17:46:21 +090068 private static final String SECURITY_GROUP_FORMAT = "%-40s%-20s";
Hyunsun Moonc7219222017-03-27 11:05:59 +090069 private static final String NETWORK_FORMAT = "%-40s%-20s%-20s%-8s";
70 private static final String SUBNET_FORMAT = "%-40s%-20s%-20s";
71 private static final String PORT_FORMAT = "%-40s%-20s%-20s%-8s";
72 private static final String ROUTER_FORMAT = "%-40s%-20s%-20s%-8s";
73 private static final String FLOATING_IP_FORMAT = "%-40s%-20s%-20s";
74
75 private static final String DEVICE_OWNER_GW = "network:router_gateway";
76 private static final String DEVICE_OWNER_IFACE = "network:router_interface";
77
Jian Li8769d5b2018-03-09 21:55:40 +090078 private static final String KEYSTONE_V2 = "v2.0";
79 private static final String KEYSTONE_V3 = "v3";
80
Hyunsun Moonc7219222017-03-27 11:05:59 +090081 @Override
82 protected void execute() {
Hyunsun Moonae51e732017-04-25 17:46:21 +090083 OpenstackSecurityGroupAdminService osSgAdminService = get(OpenstackSecurityGroupAdminService.class);
Hyunsun Moonc7219222017-03-27 11:05:59 +090084 OpenstackNetworkAdminService osNetAdminService = get(OpenstackNetworkAdminService.class);
Hyunsun Moonc7219222017-03-27 11:05:59 +090085 OpenstackRouterAdminService osRouterAdminService = get(OpenstackRouterAdminService.class);
Jian Li39057872018-04-16 11:33:12 +090086 OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
87
88 Optional<OpenstackNode> node = osNodeService.nodes(CONTROLLER).stream().findFirst();
89 if (!node.isPresent()) {
90 error("Keystone auth info has not been configured. " +
91 "Please specify auth info via network-cfg.json.");
92 return;
93 }
94
95 OpenstackAuth auth = node.get().authentication();
96 String endpoint = buildEndpoint(node.get());
97 OpenstackAuth.Perspective perspective = auth.perspective();
98
99 log.info("Access the ENDPOINT: {}, with AUTH_INFO username: {}, password: {}, project: {}",
100 endpoint, auth.username(), auth.password(), auth.project());
Hyunsun Moonc7219222017-03-27 11:05:59 +0900101
Jian Li8769d5b2018-03-09 21:55:40 +0900102 OSClient osClient;
Jian Li8cbc3b92018-04-11 12:21:25 +0900103 Config config = getSslConfig();
Jian Li1a9e71c2018-04-03 19:00:29 +0900104
Jian Li8769d5b2018-03-09 21:55:40 +0900105 try {
Jian Li39057872018-04-16 11:33:12 +0900106 if (endpoint.contains(KEYSTONE_V2)) {
107 IOSClientBuilder.V2 builder = OSFactory.builderV2()
108 .endpoint(endpoint)
109 .tenantName(auth.project())
110 .credentials(auth.username(), auth.password())
111 .withConfig(config);
Jian Li8cbc3b92018-04-11 12:21:25 +0900112
Jian Li39057872018-04-16 11:33:12 +0900113 if (perspective != null) {
114 builder.perspective(getFacing(perspective));
Jian Li8769d5b2018-03-09 21:55:40 +0900115 }
Jian Li39057872018-04-16 11:33:12 +0900116
117 osClient = builder.authenticate();
118 } else if (endpoint.contains(KEYSTONE_V3)) {
119
120 Identifier project = Identifier.byName(auth.project());
121 Identifier domain = Identifier.byName(DOMAIN_DEFUALT);
122
123 IOSClientBuilder.V3 builder = OSFactory.builderV3()
124 .endpoint(endpoint)
125 .credentials(auth.username(), auth.password(), domain)
126 .scopeToProject(project, domain)
127 .withConfig(config);
128
129 if (perspective != null) {
130 builder.perspective(getFacing(perspective));
131 }
132
133 osClient = builder.authenticate();
Jian Li8769d5b2018-03-09 21:55:40 +0900134 } else {
Jian Li39057872018-04-16 11:33:12 +0900135 print("Unrecognized keystone version type");
Jian Li8769d5b2018-03-09 21:55:40 +0900136 return;
137 }
Hyunsun Moonc7219222017-03-27 11:05:59 +0900138 } catch (AuthenticationException e) {
139 print("Authentication failed");
140 return;
Hyunsun Moonc7219222017-03-27 11:05:59 +0900141 }
142
Hyunsun Moonae51e732017-04-25 17:46:21 +0900143 print("Synchronizing OpenStack security groups");
144 print(SECURITY_GROUP_FORMAT, "ID", "Name");
145 osClient.networking().securitygroup().list().forEach(osSg -> {
Jian Li21536592018-03-06 15:10:55 +0900146 if (osSgAdminService.securityGroup(osSg.getId()) != null) {
Hyunsun Moonae51e732017-04-25 17:46:21 +0900147 osSgAdminService.updateSecurityGroup(osSg);
148 } else {
149 osSgAdminService.createSecurityGroup(osSg);
150 }
151 print(SECURITY_GROUP_FORMAT, osSg.getId(), osSg.getName());
152 });
153
154 print("\nSynchronizing OpenStack networks");
Hyunsun Moonc7219222017-03-27 11:05:59 +0900155 print(NETWORK_FORMAT, "ID", "Name", "VNI", "Subnets");
156 osClient.networking().network().list().forEach(osNet -> {
Jian Li5a15fe62018-03-06 13:41:20 +0900157 if (osNetAdminService.network(osNet.getId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900158 osNetAdminService.updateNetwork(osNet);
159 } else {
160 osNetAdminService.createNetwork(osNet);
161 }
162 printNetwork(osNet);
163 });
164
Hyunsun Moonae51e732017-04-25 17:46:21 +0900165 print("\nSynchronizing OpenStack subnets");
Hyunsun Moonc7219222017-03-27 11:05:59 +0900166 print(SUBNET_FORMAT, "ID", "Network", "CIDR");
167 osClient.networking().subnet().list().forEach(osSubnet -> {
Jian Li5a15fe62018-03-06 13:41:20 +0900168 if (osNetAdminService.subnet(osSubnet.getId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900169 osNetAdminService.updateSubnet(osSubnet);
170 } else {
171 osNetAdminService.createSubnet(osSubnet);
172 }
Jian Li5a15fe62018-03-06 13:41:20 +0900173 printSubnet(osSubnet, osNetAdminService);
Hyunsun Moonc7219222017-03-27 11:05:59 +0900174 });
175
Hyunsun Moonae51e732017-04-25 17:46:21 +0900176 print("\nSynchronizing OpenStack ports");
Hyunsun Moonc7219222017-03-27 11:05:59 +0900177 print(PORT_FORMAT, "ID", "Network", "MAC", "Fixed IPs");
178 osClient.networking().port().list().forEach(osPort -> {
Jian Li5a15fe62018-03-06 13:41:20 +0900179 if (osNetAdminService.port(osPort.getId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900180 osNetAdminService.updatePort(osPort);
181 } else {
182 osNetAdminService.createPort(osPort);
183 }
Jian Li5a15fe62018-03-06 13:41:20 +0900184 printPort(osPort, osNetAdminService);
Hyunsun Moonc7219222017-03-27 11:05:59 +0900185 });
186
Hyunsun Moonae51e732017-04-25 17:46:21 +0900187 print("\nSynchronizing OpenStack routers");
Hyunsun Moonc7219222017-03-27 11:05:59 +0900188 print(ROUTER_FORMAT, "ID", "Name", "External", "Internal");
189 osClient.networking().router().list().forEach(osRouter -> {
Jian Lida7c6cb2018-03-06 14:58:54 +0900190 if (osRouterAdminService.router(osRouter.getId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900191 osRouterAdminService.updateRouter(osRouter);
192 } else {
193 osRouterAdminService.createRouter(osRouter);
194 }
195
196 // FIXME do we need to manage router interfaces separately?
Jian Li5a15fe62018-03-06 13:41:20 +0900197 osNetAdminService.ports().stream()
Hyunsun Moonc7219222017-03-27 11:05:59 +0900198 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
199 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
Jian Lida7c6cb2018-03-06 14:58:54 +0900200 .forEach(osPort -> addRouterIface(osPort, osRouterAdminService));
Hyunsun Moonc7219222017-03-27 11:05:59 +0900201
Jian Li5a15fe62018-03-06 13:41:20 +0900202 printRouter(osRouter, osNetAdminService);
Hyunsun Moonc7219222017-03-27 11:05:59 +0900203 });
204
Hyunsun Moonae51e732017-04-25 17:46:21 +0900205 print("\nSynchronizing OpenStack floating IPs");
Hyunsun Moonc7219222017-03-27 11:05:59 +0900206 print(FLOATING_IP_FORMAT, "ID", "Floating IP", "Fixed IP");
207 osClient.networking().floatingip().list().forEach(osFloating -> {
Jian Lida7c6cb2018-03-06 14:58:54 +0900208 if (osRouterAdminService.floatingIp(osFloating.getId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900209 osRouterAdminService.updateFloatingIp(osFloating);
210 } else {
211 osRouterAdminService.createFloatingIp(osFloating);
212 }
213 printFloatingIp(osFloating);
214 });
215 }
216
217 // TODO fix the logic to add router interface to router
Jian Lida7c6cb2018-03-06 14:58:54 +0900218 private void addRouterIface(Port osPort, OpenstackRouterAdminService adminService) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900219 osPort.getFixedIps().forEach(p -> {
220 JsonNode jsonTree = mapper().createObjectNode()
221 .put("id", osPort.getDeviceId())
222 .put("tenant_id", osPort.getTenantId())
223 .put("subnet_id", p.getSubnetId())
224 .put("port_id", osPort.getId());
225 try {
226 RouterInterface rIface = getContext(NeutronRouterInterface.class)
227 .readerFor(NeutronRouterInterface.class)
228 .readValue(jsonTree);
Jian Lida7c6cb2018-03-06 14:58:54 +0900229 if (adminService.routerInterface(rIface.getPortId()) != null) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900230 adminService.updateRouterInterface(rIface);
231 } else {
232 adminService.addRouterInterface(rIface);
233 }
234 } catch (IOException ignore) {
235 }
236 });
237 }
238
Jian Li39057872018-04-16 11:33:12 +0900239 private String buildEndpoint(OpenstackNode node) {
240
241 OpenstackAuth auth = node.authentication();
242
243 StringBuilder endpointSb = new StringBuilder();
244 endpointSb.append(auth.protocol().name().toLowerCase());
245 endpointSb.append("://");
246 endpointSb.append(node.managementIp());
247 endpointSb.append(":");
248 endpointSb.append(auth.port());
249 endpointSb.append("/");
250
251 // in case the version is v3, we need to append identity path into endpoint
252 if (auth.version().equals("v3")) {
253 endpointSb.append("identity/");
254 }
255
256 endpointSb.append(auth.version());
257 return endpointSb.toString();
258 }
259
Hyunsun Moonc7219222017-03-27 11:05:59 +0900260 private void printNetwork(Network osNet) {
261 final String strNet = String.format(NETWORK_FORMAT,
262 osNet.getId(),
263 osNet.getName(),
264 osNet.getProviderSegID(),
265 osNet.getSubnets());
266 print(strNet);
267 }
268
Jian Li5a15fe62018-03-06 13:41:20 +0900269 private void printSubnet(Subnet osSubnet, OpenstackNetworkAdminService osNetService) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900270 final String strSubnet = String.format(SUBNET_FORMAT,
271 osSubnet.getId(),
272 osNetService.network(osSubnet.getNetworkId()).getName(),
273 osSubnet.getCidr());
274 print(strSubnet);
275 }
276
Jian Li5a15fe62018-03-06 13:41:20 +0900277 private void printPort(Port osPort, OpenstackNetworkAdminService osNetService) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900278 List<String> fixedIps = osPort.getFixedIps().stream()
279 .map(IP::getIpAddress)
280 .collect(Collectors.toList());
281 final String strPort = String.format(PORT_FORMAT,
282 osPort.getId(),
283 osNetService.network(osPort.getNetworkId()).getName(),
284 osPort.getMacAddress(),
285 fixedIps.isEmpty() ? "" : fixedIps);
286 print(strPort);
287 }
288
Jian Li5a15fe62018-03-06 13:41:20 +0900289 private void printRouter(Router osRouter, OpenstackNetworkAdminService osNetService) {
Hyunsun Moonc7219222017-03-27 11:05:59 +0900290 List<String> externals = osNetService.ports().stream()
291 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
292 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_GW))
293 .flatMap(osPort -> osPort.getFixedIps().stream())
294 .map(IP::getIpAddress)
295 .collect(Collectors.toList());
296
297 List<String> internals = osNetService.ports().stream()
298 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
299 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
300 .flatMap(osPort -> osPort.getFixedIps().stream())
301 .map(IP::getIpAddress)
302 .collect(Collectors.toList());
303
304 final String strRouter = String.format(ROUTER_FORMAT,
305 osRouter.getId(),
306 osRouter.getName(),
307 externals.isEmpty() ? "" : externals,
308 internals.isEmpty() ? "" : internals);
309 print(strRouter);
310 }
311
312 private void printFloatingIp(NetFloatingIP floatingIp) {
313 final String strFloating = String.format(FLOATING_IP_FORMAT,
314 floatingIp.getId(),
daniel parkeeb8e042018-02-21 14:06:58 +0900315 floatingIp.getFloatingIpAddress(),
Hyunsun Moonc7219222017-03-27 11:05:59 +0900316 Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
317 "" : floatingIp.getFixedIpAddress());
318 print(strFloating);
319 }
Jian Lib43b0672018-04-05 21:48:51 +0900320
Jian Li8cbc3b92018-04-11 12:21:25 +0900321 private Config getSslConfig() {
322 // we bypass the SSL certification verification for now
323 // TODO: verify server side SSL using a given certification
324 Config config = Config.newConfig().withSSLVerificationDisabled();
325
326 TrustManager[] trustAllCerts = new TrustManager[]{
327 new X509TrustManager() {
328 public X509Certificate[] getAcceptedIssuers() {
329 return null;
330 }
331
332 public void checkClientTrusted(X509Certificate[] certs, String authType) {
333 }
334
335 public void checkServerTrusted(X509Certificate[] certs, String authType) {
336 }
337 }
338 };
339
340 HostnameVerifier allHostsValid = (hostname, session) -> true;
341
342 try {
343 SSLContext sc = SSLContext.getInstance("SSL");
344 sc.init(null, trustAllCerts, new java.security.SecureRandom());
345 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
346 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
347
348 config.withSSLContext(sc);
349 } catch (Exception e) {
350 print("Failed to access OpenStack service");
351 return null;
352 }
353
354 return config;
355 }
356
Jian Li39057872018-04-16 11:33:12 +0900357 private Facing getFacing(OpenstackAuth.Perspective perspective) {
Jian Lib43b0672018-04-05 21:48:51 +0900358
Jian Li39057872018-04-16 11:33:12 +0900359 switch (perspective) {
360 case PUBLIC:
Jian Lib43b0672018-04-05 21:48:51 +0900361 return Facing.PUBLIC;
Jian Li39057872018-04-16 11:33:12 +0900362 case ADMIN:
Jian Lib43b0672018-04-05 21:48:51 +0900363 return Facing.ADMIN;
Jian Li39057872018-04-16 11:33:12 +0900364 case INTERNAL:
Jian Lib43b0672018-04-05 21:48:51 +0900365 return Facing.INTERNAL;
366 default:
367 return null;
368 }
369 }
Hyunsun Moonc7219222017-03-27 11:05:59 +0900370}