blob: febcb7a45ef6f2d83103e6af885330e2621d6e7a [file] [log] [blame]
Jian Li0b564282018-06-20 00:50:53 +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.openstacknetworking.web;
17
Jian Li803a1d52018-06-21 21:47:48 +090018import com.fasterxml.jackson.databind.node.ArrayNode;
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import com.google.common.base.Strings;
21import com.google.common.collect.Lists;
Jian Li0b564282018-06-20 00:50:53 +090022import org.onlab.util.ItemNotFoundException;
Jian Li7f024de2018-07-07 03:51:02 +090023import org.onosproject.cfg.ComponentConfigService;
Jian Li0b564282018-06-20 00:50:53 +090024import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.flow.FlowRuleService;
27import org.onosproject.openstacknetworking.api.Constants;
28import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
29import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
30import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
Jian Li7f024de2018-07-07 03:51:02 +090031import org.onosproject.openstacknetworking.impl.OpenstackRoutingArpHandler;
Jian Licad36c72018-09-13 17:44:54 +090032import org.onosproject.openstacknetworking.impl.OpenstackSecurityGroupHandler;
Jian Li7f024de2018-07-07 03:51:02 +090033import org.onosproject.openstacknetworking.impl.OpenstackSwitchingArpHandler;
Jian Li0b564282018-06-20 00:50:53 +090034import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
35import org.onosproject.openstacknode.api.NodeState;
36import org.onosproject.openstacknode.api.OpenstackNode;
37import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
Jian Li0b564282018-06-20 00:50:53 +090038import org.onosproject.rest.AbstractWebResource;
39import org.openstack4j.api.OSClient;
Jian Li803a1d52018-06-21 21:47:48 +090040import org.openstack4j.model.network.NetFloatingIP;
Jian Li0b564282018-06-20 00:50:53 +090041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44import javax.ws.rs.GET;
45import javax.ws.rs.Path;
Jian Li7f024de2018-07-07 03:51:02 +090046import javax.ws.rs.PathParam;
Jian Li0b564282018-06-20 00:50:53 +090047import javax.ws.rs.Produces;
48import javax.ws.rs.core.MediaType;
49import javax.ws.rs.core.Response;
Jian Li803a1d52018-06-21 21:47:48 +090050import java.util.Comparator;
sanghoshin2a354e32018-11-09 12:52:44 +080051import java.util.HashMap;
Jian Li803a1d52018-06-21 21:47:48 +090052import java.util.List;
sanghoshin2a354e32018-11-09 12:52:44 +080053import java.util.Map;
Jian Li0b564282018-06-20 00:50:53 +090054import java.util.Objects;
55import java.util.Optional;
56
Jian Lif7934d52018-07-10 16:27:02 +090057import static java.lang.Thread.sleep;
Jian Li167f0c42018-11-18 11:03:51 +090058import static java.util.stream.StreamSupport.stream;
Jian Li7f024de2018-07-07 03:51:02 +090059import static org.onlab.util.Tools.nullIsIllegal;
Jian Li5c777c62018-11-11 00:31:20 +090060import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.addRouterIface;
61import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.checkActivationFlag;
62import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.checkArpMode;
63import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Li8b5599b2018-11-19 18:45:46 +090064import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
Jian Li5c777c62018-11-11 00:31:20 +090065import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Jian Li0b564282018-06-20 00:50:53 +090066import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Jian Li5c777c62018-11-11 00:31:20 +090067import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Jian Li0b564282018-06-20 00:50:53 +090068
69/**
70 * REST interface for synchronizing openstack network states and rules.
71 */
72@Path("management")
73public class OpenstackManagementWebResource extends AbstractWebResource {
74 private final Logger log = LoggerFactory.getLogger(getClass());
75
Jian Li803a1d52018-06-21 21:47:48 +090076 private static final String FLOATINGIPS = "floatingips";
Jian Li7f024de2018-07-07 03:51:02 +090077 private static final String ARP_MODE_NAME = "arpMode";
Jian Licad36c72018-09-13 17:44:54 +090078 private static final String USE_SECURITY_GROUP_NAME = "useSecurityGroup";
Jian Li803a1d52018-06-21 21:47:48 +090079
Jian Lif7934d52018-07-10 16:27:02 +090080 private static final long SLEEP_MS = 3000; // we wait 3s for init each node
Jian Li167f0c42018-11-18 11:03:51 +090081 private static final long TIMEOUT_MS = 10000; // we wait 10s
Jian Li88ae51e2018-07-10 02:23:35 +090082
Jian Li0b564282018-06-20 00:50:53 +090083 private static final String DEVICE_OWNER_IFACE = "network:router_interface";
Jian Li7f024de2018-07-07 03:51:02 +090084
85 private static final String ARP_MODE_REQUIRED = "ARP mode is not specified";
86
Jian Licad36c72018-09-13 17:44:54 +090087 private static final String SECURITY_GROUP_FLAG_REQUIRED = "Security Group flag is not specified";
88
sanghoshin2a354e32018-11-09 12:52:44 +080089 private static final String HTTP_HEADER_ACCEPT = "accept";
90 private static final String HTTP_HEADER_VALUE_JSON = "application/json";
91
Jian Li803a1d52018-06-21 21:47:48 +090092 private final ObjectNode root = mapper().createObjectNode();
93 private final ArrayNode floatingipsNode = root.putArray(FLOATINGIPS);
Jian Li0b564282018-06-20 00:50:53 +090094
95 private final OpenstackSecurityGroupAdminService osSgAdminService =
96 get(OpenstackSecurityGroupAdminService.class);
97 private final OpenstackNetworkAdminService osNetAdminService =
98 get(OpenstackNetworkAdminService.class);
99 private final OpenstackRouterAdminService osRouterAdminService =
100 get(OpenstackRouterAdminService.class);
Jian Li0b564282018-06-20 00:50:53 +0900101 private final OpenstackNodeAdminService osNodeAdminService =
102 get(OpenstackNodeAdminService.class);
103 private final FlowRuleService flowRuleService = get(FlowRuleService.class);
104 private final CoreService coreService = get(CoreService.class);
105
106 /**
107 * Synchronizes the network states with openstack.
108 *
109 * @return 200 OK with sync result, 404 not found
110 */
111 @GET
112 @Produces(MediaType.APPLICATION_JSON)
113 @Path("sync/states")
114 public Response syncStates() {
115
sanghoshin2a354e32018-11-09 12:52:44 +0800116 Map<String, String> headerMap = new HashMap();
117 headerMap.put(HTTP_HEADER_ACCEPT, HTTP_HEADER_VALUE_JSON);
118
Jian Li78ac0652018-07-17 18:10:16 +0900119 Optional<OpenstackNode> node = osNodeAdminService.nodes(CONTROLLER).stream().findFirst();
Jian Li0b564282018-06-20 00:50:53 +0900120 if (!node.isPresent()) {
121 throw new ItemNotFoundException("Auth info is not found");
122 }
123
124 OSClient osClient = OpenstackNetworkingUtil.getConnectedClient(node.get());
125
126 if (osClient == null) {
127 throw new ItemNotFoundException("Auth info is not correct");
128 }
129
sanghoshin2a354e32018-11-09 12:52:44 +0800130 osClient.headers(headerMap).networking().securitygroup().list().forEach(osSg -> {
Jian Li0b564282018-06-20 00:50:53 +0900131 if (osSgAdminService.securityGroup(osSg.getId()) != null) {
132 osSgAdminService.updateSecurityGroup(osSg);
133 } else {
134 osSgAdminService.createSecurityGroup(osSg);
135 }
136 });
137
sanghoshin2a354e32018-11-09 12:52:44 +0800138 osClient.headers(headerMap).networking().network().list().forEach(osNet -> {
Jian Li0b564282018-06-20 00:50:53 +0900139 if (osNetAdminService.network(osNet.getId()) != null) {
140 osNetAdminService.updateNetwork(osNet);
141 } else {
142 osNetAdminService.createNetwork(osNet);
143 }
144 });
145
sanghoshin2a354e32018-11-09 12:52:44 +0800146 osClient.headers(headerMap).networking().subnet().list().forEach(osSubnet -> {
Jian Li0b564282018-06-20 00:50:53 +0900147 if (osNetAdminService.subnet(osSubnet.getId()) != null) {
148 osNetAdminService.updateSubnet(osSubnet);
149 } else {
150 osNetAdminService.createSubnet(osSubnet);
151 }
152 });
153
sanghoshin2a354e32018-11-09 12:52:44 +0800154 osClient.headers(headerMap).networking().port().list().forEach(osPort -> {
Jian Li0b564282018-06-20 00:50:53 +0900155 if (osNetAdminService.port(osPort.getId()) != null) {
156 osNetAdminService.updatePort(osPort);
157 } else {
158 osNetAdminService.createPort(osPort);
159 }
160 });
161
sanghoshin2a354e32018-11-09 12:52:44 +0800162 osClient.headers(headerMap).networking().router().list().forEach(osRouter -> {
Jian Li0b564282018-06-20 00:50:53 +0900163 if (osRouterAdminService.router(osRouter.getId()) != null) {
164 osRouterAdminService.updateRouter(osRouter);
165 } else {
166 osRouterAdminService.createRouter(osRouter);
167 }
168
169 osNetAdminService.ports().stream()
170 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
171 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
172 .forEach(osPort -> addRouterIface(osPort, osRouterAdminService));
173 });
174
sanghoshin2a354e32018-11-09 12:52:44 +0800175 osClient.headers(headerMap).networking().floatingip().list().forEach(osFloating -> {
Jian Li0b564282018-06-20 00:50:53 +0900176 if (osRouterAdminService.floatingIp(osFloating.getId()) != null) {
177 osRouterAdminService.updateFloatingIp(osFloating);
178 } else {
179 osRouterAdminService.createFloatingIp(osFloating);
180 }
181 });
182
183 return ok(mapper().createObjectNode()).build();
184 }
185
186 /**
187 * Synchronizes the flow rules.
188 *
189 * @return 200 OK with sync result, 404 not found
190 */
191 @GET
192 @Produces(MediaType.APPLICATION_JSON)
193 @Path("sync/rules")
194 public Response syncRules() {
195
Jian Li7f024de2018-07-07 03:51:02 +0900196 syncRulesBase();
Jian Li0b564282018-06-20 00:50:53 +0900197 return ok(mapper().createObjectNode()).build();
198 }
199
200 /**
201 * Purges the network states.
202 *
203 * @return 200 OK with purge result, 404 not found
204 */
205 @GET
206 @Produces(MediaType.APPLICATION_JSON)
207 @Path("purge/states")
208 public Response purgeStates() {
209
210 osRouterAdminService.clear();
211 osNetAdminService.clear();
212 osSgAdminService.clear();
213
214 return ok(mapper().createObjectNode()).build();
215 }
216
217 /**
218 * Purges the flow rules installed by openstacknetworking.
219 *
220 * @return 200 OK with purge result, 404 not found
221 */
222 @GET
223 @Produces(MediaType.APPLICATION_JSON)
224 @Path("purge/rules")
225 public Response purgeRules() {
226
Jian Li167f0c42018-11-18 11:03:51 +0900227 if (purgeRulesBase()) {
228 return ok(mapper().createObjectNode()).build();
229 } else {
230 return Response.serverError().build();
231 }
Jian Li7f024de2018-07-07 03:51:02 +0900232 }
233
234 /**
235 * Configures the ARP mode (proxy | broadcast).
236 *
237 * @param arpmode ARP mode
238 * @return 200 OK with config result, 404 not found
239 */
240 @GET
241 @Produces(MediaType.APPLICATION_JSON)
242 @Path("config/arpmode/{arpmode}")
243 public Response configArpMode(@PathParam("arpmode") String arpmode) {
244
245 String arpModeStr = nullIsIllegal(arpmode, ARP_MODE_REQUIRED);
246 if (checkArpMode(arpModeStr)) {
247 configArpModeBase(arpModeStr);
248
249 ComponentConfigService service = get(ComponentConfigService.class);
250 String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
251 String routingComponent = OpenstackRoutingArpHandler.class.getName();
252
253 // we check the arpMode configured in each component, and purge and
254 // reinstall all rules only if the arpMode is changed to the configured one
255 while (true) {
256 String switchingValue =
257 getPropertyValue(service.getProperties(switchingComponent), ARP_MODE_NAME);
258 String routingValue =
259 getPropertyValue(service.getProperties(routingComponent), ARP_MODE_NAME);
260
261 if (arpModeStr.equals(switchingValue) && arpModeStr.equals(routingValue)) {
262 break;
263 }
264 }
265
266 purgeRulesBase();
267 syncRulesBase();
268 } else {
269 throw new IllegalArgumentException("The ARP mode is not valid");
Jian Li0b564282018-06-20 00:50:53 +0900270 }
Jian Li0b564282018-06-20 00:50:53 +0900271
272 return ok(mapper().createObjectNode()).build();
273 }
Jian Li803a1d52018-06-21 21:47:48 +0900274
275 /**
Jian Licad36c72018-09-13 17:44:54 +0900276 * Configures the security group (enable | disable).
277 *
278 * @param securityGroup security group activation flag
279 * @return 200 OK with config result, 404 not found
280 */
281 @GET
282 @Produces(MediaType.APPLICATION_JSON)
283 @Path("config/securityGroup/{securityGroup}")
284 public Response configSecurityGroup(@PathParam("securityGroup") String securityGroup) {
285 String securityGroupStr = nullIsIllegal(securityGroup, SECURITY_GROUP_FLAG_REQUIRED);
286
287 boolean flag = checkActivationFlag(securityGroupStr);
288
289 ComponentConfigService service = get(ComponentConfigService.class);
290 String securityGroupComponent = OpenstackSecurityGroupHandler.class.getName();
291
292 service.setProperty(securityGroupComponent, USE_SECURITY_GROUP_NAME, String.valueOf(flag));
293
294 return ok(mapper().createObjectNode()).build();
295 }
296
297 /**
Jian Li803a1d52018-06-21 21:47:48 +0900298 * Obtains a collection of all floating IPs.
299 *
300 * @return 200 OK with a collection of floating IPs, 404 not found
301 */
302 @GET
303 @Produces(MediaType.APPLICATION_JSON)
304 @Path("floatingips/all")
305 public Response allFloatingIps() {
306
307 List<NetFloatingIP> floatingIps =
308 Lists.newArrayList(osRouterAdminService.floatingIps());
309 floatingIps.stream()
310 .sorted(Comparator.comparing(NetFloatingIP::getFloatingIpAddress))
311 .forEach(fip -> floatingipsNode.add(fip.getFloatingIpAddress()));
312
313 return ok(root).build();
314 }
315
316 /**
317 * Obtains a collection of all floating IPs mapped with fixed IPs.
318 *
319 * @return 200 OK with a collection of floating IPs mapped with fixed IPs,
320 * 404 not found
321 */
322 @GET
323 @Produces(MediaType.APPLICATION_JSON)
324 @Path("floatingips/mapped")
325 public Response mappedFloatingIps() {
326
327 List<NetFloatingIP> floatingIps =
328 Lists.newArrayList(osRouterAdminService.floatingIps());
329
330 floatingIps.stream()
331 .filter(fip -> !Strings.isNullOrEmpty(fip.getFixedIpAddress()))
332 .sorted(Comparator.comparing(NetFloatingIP::getFloatingIpAddress))
333 .forEach(fip -> floatingipsNode.add(fip.getFloatingIpAddress()));
334
335 return ok(root).build();
336 }
Jian Li7f024de2018-07-07 03:51:02 +0900337
338 private void syncRulesBase() {
Jian Li5c777c62018-11-11 00:31:20 +0900339 // we first initialize the COMPUTE node, in order to feed all instance ports
340 // by referring to ports' information obtained from neutron server
341 osNodeAdminService.completeNodes(COMPUTE).forEach(this::syncRulesBaseForNode);
342 osNodeAdminService.completeNodes(GATEWAY).forEach(this::syncRulesBaseForNode);
343 }
Jian Li88ae51e2018-07-10 02:23:35 +0900344
Jian Li5c777c62018-11-11 00:31:20 +0900345 private void syncRulesBaseForNode(OpenstackNode osNode) {
346 OpenstackNode updated = osNode.updateState(NodeState.INIT);
347 osNodeAdminService.updateNode(updated);
Jian Li88ae51e2018-07-10 02:23:35 +0900348
Jian Li8b5599b2018-11-19 18:45:46 +0900349 boolean result = true;
350 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
351
352 while (osNodeAdminService.node(osNode.hostname()).state() != COMPLETE) {
353
354 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
355
356 try {
357 sleep(SLEEP_MS);
358 } catch (InterruptedException e) {
359 log.error("Exception caused during node synchronization...");
360 }
361
362 if (osNodeAdminService.node(osNode.hostname()).state() == COMPLETE) {
363 break;
364 } else {
365 osNodeAdminService.updateNode(updated);
366 log.info("Failed to synchronize flow rules, retrying...");
367 }
368
369 if (waitMs <= 0) {
370 result = false;
371 break;
372 }
Jian Li5c777c62018-11-11 00:31:20 +0900373 }
374
Jian Li8b5599b2018-11-19 18:45:46 +0900375 if (result) {
376 log.info("Successfully synchronize flow rules for node {}!", osNode.hostname());
Jian Li5c777c62018-11-11 00:31:20 +0900377 } else {
Jian Li8b5599b2018-11-19 18:45:46 +0900378 log.warn("Failed to synchronize flow rules for node {}.", osNode.hostname());
Jian Li5c777c62018-11-11 00:31:20 +0900379 }
Jian Li7f024de2018-07-07 03:51:02 +0900380 }
381
Jian Li167f0c42018-11-18 11:03:51 +0900382 private boolean purgeRulesBase() {
Jian Li7f024de2018-07-07 03:51:02 +0900383 ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
384 if (appId == null) {
385 throw new ItemNotFoundException("application not found");
386 }
Jian Li167f0c42018-11-18 11:03:51 +0900387
Jian Li7f024de2018-07-07 03:51:02 +0900388 flowRuleService.removeFlowRulesById(appId);
Jian Li167f0c42018-11-18 11:03:51 +0900389
390 boolean result = true;
391 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
392
393 // we make sure all flow rules are removed from the store
394 while (stream(flowRuleService.getFlowEntriesById(appId)
395 .spliterator(), false).count() > 0) {
396
397 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
398
399 try {
400 sleep(SLEEP_MS);
401 } catch (InterruptedException e) {
402 log.error("Exception caused during rule purging...");
403 }
404
405 if (stream(flowRuleService.getFlowEntriesById(appId)
406 .spliterator(), false).count() == 0) {
407 break;
408 } else {
409 flowRuleService.removeFlowRulesById(appId);
410 log.info("Failed to purging flow rules, retrying rule purging...");
411 }
412
413 if (waitMs <= 0) {
414 result = false;
415 break;
416 }
417 }
418
419 if (result) {
420 log.info("Successfully purged flow rules!");
421 } else {
422 log.warn("Failed to purge flow rules.");
423 }
424
425 return result;
Jian Li7f024de2018-07-07 03:51:02 +0900426 }
427
428 private void configArpModeBase(String arpMode) {
429 ComponentConfigService service = get(ComponentConfigService.class);
430 String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
431 String routingComponent = OpenstackRoutingArpHandler.class.getName();
432
433 service.setProperty(switchingComponent, ARP_MODE_NAME, arpMode);
434 service.setProperty(routingComponent, ARP_MODE_NAME, arpMode);
435 }
Jian Li0b564282018-06-20 00:50:53 +0900436}