blob: bd828aa406d03a044841ec3fd2e527a5f83eb150 [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;
64import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Jian Li0b564282018-06-20 00:50:53 +090065import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Jian Li5c777c62018-11-11 00:31:20 +090066import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Jian Li0b564282018-06-20 00:50:53 +090067
68/**
69 * REST interface for synchronizing openstack network states and rules.
70 */
71@Path("management")
72public class OpenstackManagementWebResource extends AbstractWebResource {
73 private final Logger log = LoggerFactory.getLogger(getClass());
74
Jian Li803a1d52018-06-21 21:47:48 +090075 private static final String FLOATINGIPS = "floatingips";
Jian Li7f024de2018-07-07 03:51:02 +090076 private static final String ARP_MODE_NAME = "arpMode";
Jian Licad36c72018-09-13 17:44:54 +090077 private static final String USE_SECURITY_GROUP_NAME = "useSecurityGroup";
Jian Li803a1d52018-06-21 21:47:48 +090078
Jian Lif7934d52018-07-10 16:27:02 +090079 private static final long SLEEP_MS = 3000; // we wait 3s for init each node
Jian Li167f0c42018-11-18 11:03:51 +090080 private static final long TIMEOUT_MS = 10000; // we wait 10s
Jian Li88ae51e2018-07-10 02:23:35 +090081
Jian Li0b564282018-06-20 00:50:53 +090082 private static final String DEVICE_OWNER_IFACE = "network:router_interface";
Jian Li7f024de2018-07-07 03:51:02 +090083
84 private static final String ARP_MODE_REQUIRED = "ARP mode is not specified";
85
Jian Licad36c72018-09-13 17:44:54 +090086 private static final String SECURITY_GROUP_FLAG_REQUIRED = "Security Group flag is not specified";
87
sanghoshin2a354e32018-11-09 12:52:44 +080088 private static final String HTTP_HEADER_ACCEPT = "accept";
89 private static final String HTTP_HEADER_VALUE_JSON = "application/json";
90
Jian Li803a1d52018-06-21 21:47:48 +090091 private final ObjectNode root = mapper().createObjectNode();
92 private final ArrayNode floatingipsNode = root.putArray(FLOATINGIPS);
Jian Li0b564282018-06-20 00:50:53 +090093
94 private final OpenstackSecurityGroupAdminService osSgAdminService =
95 get(OpenstackSecurityGroupAdminService.class);
96 private final OpenstackNetworkAdminService osNetAdminService =
97 get(OpenstackNetworkAdminService.class);
98 private final OpenstackRouterAdminService osRouterAdminService =
99 get(OpenstackRouterAdminService.class);
Jian Li0b564282018-06-20 00:50:53 +0900100 private final OpenstackNodeAdminService osNodeAdminService =
101 get(OpenstackNodeAdminService.class);
102 private final FlowRuleService flowRuleService = get(FlowRuleService.class);
103 private final CoreService coreService = get(CoreService.class);
104
105 /**
106 * Synchronizes the network states with openstack.
107 *
108 * @return 200 OK with sync result, 404 not found
109 */
110 @GET
111 @Produces(MediaType.APPLICATION_JSON)
112 @Path("sync/states")
113 public Response syncStates() {
114
sanghoshin2a354e32018-11-09 12:52:44 +0800115 Map<String, String> headerMap = new HashMap();
116 headerMap.put(HTTP_HEADER_ACCEPT, HTTP_HEADER_VALUE_JSON);
117
Jian Li78ac0652018-07-17 18:10:16 +0900118 Optional<OpenstackNode> node = osNodeAdminService.nodes(CONTROLLER).stream().findFirst();
Jian Li0b564282018-06-20 00:50:53 +0900119 if (!node.isPresent()) {
120 throw new ItemNotFoundException("Auth info is not found");
121 }
122
123 OSClient osClient = OpenstackNetworkingUtil.getConnectedClient(node.get());
124
125 if (osClient == null) {
126 throw new ItemNotFoundException("Auth info is not correct");
127 }
128
sanghoshin2a354e32018-11-09 12:52:44 +0800129 osClient.headers(headerMap).networking().securitygroup().list().forEach(osSg -> {
Jian Li0b564282018-06-20 00:50:53 +0900130 if (osSgAdminService.securityGroup(osSg.getId()) != null) {
131 osSgAdminService.updateSecurityGroup(osSg);
132 } else {
133 osSgAdminService.createSecurityGroup(osSg);
134 }
135 });
136
sanghoshin2a354e32018-11-09 12:52:44 +0800137 osClient.headers(headerMap).networking().network().list().forEach(osNet -> {
Jian Li0b564282018-06-20 00:50:53 +0900138 if (osNetAdminService.network(osNet.getId()) != null) {
139 osNetAdminService.updateNetwork(osNet);
140 } else {
141 osNetAdminService.createNetwork(osNet);
142 }
143 });
144
sanghoshin2a354e32018-11-09 12:52:44 +0800145 osClient.headers(headerMap).networking().subnet().list().forEach(osSubnet -> {
Jian Li0b564282018-06-20 00:50:53 +0900146 if (osNetAdminService.subnet(osSubnet.getId()) != null) {
147 osNetAdminService.updateSubnet(osSubnet);
148 } else {
149 osNetAdminService.createSubnet(osSubnet);
150 }
151 });
152
sanghoshin2a354e32018-11-09 12:52:44 +0800153 osClient.headers(headerMap).networking().port().list().forEach(osPort -> {
Jian Li0b564282018-06-20 00:50:53 +0900154 if (osNetAdminService.port(osPort.getId()) != null) {
155 osNetAdminService.updatePort(osPort);
156 } else {
157 osNetAdminService.createPort(osPort);
158 }
159 });
160
sanghoshin2a354e32018-11-09 12:52:44 +0800161 osClient.headers(headerMap).networking().router().list().forEach(osRouter -> {
Jian Li0b564282018-06-20 00:50:53 +0900162 if (osRouterAdminService.router(osRouter.getId()) != null) {
163 osRouterAdminService.updateRouter(osRouter);
164 } else {
165 osRouterAdminService.createRouter(osRouter);
166 }
167
168 osNetAdminService.ports().stream()
169 .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
170 Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
171 .forEach(osPort -> addRouterIface(osPort, osRouterAdminService));
172 });
173
sanghoshin2a354e32018-11-09 12:52:44 +0800174 osClient.headers(headerMap).networking().floatingip().list().forEach(osFloating -> {
Jian Li0b564282018-06-20 00:50:53 +0900175 if (osRouterAdminService.floatingIp(osFloating.getId()) != null) {
176 osRouterAdminService.updateFloatingIp(osFloating);
177 } else {
178 osRouterAdminService.createFloatingIp(osFloating);
179 }
180 });
181
182 return ok(mapper().createObjectNode()).build();
183 }
184
185 /**
186 * Synchronizes the flow rules.
187 *
188 * @return 200 OK with sync result, 404 not found
189 */
190 @GET
191 @Produces(MediaType.APPLICATION_JSON)
192 @Path("sync/rules")
193 public Response syncRules() {
194
Jian Li7f024de2018-07-07 03:51:02 +0900195 syncRulesBase();
Jian Li0b564282018-06-20 00:50:53 +0900196 return ok(mapper().createObjectNode()).build();
197 }
198
199 /**
200 * Purges the network states.
201 *
202 * @return 200 OK with purge result, 404 not found
203 */
204 @GET
205 @Produces(MediaType.APPLICATION_JSON)
206 @Path("purge/states")
207 public Response purgeStates() {
208
209 osRouterAdminService.clear();
210 osNetAdminService.clear();
211 osSgAdminService.clear();
212
213 return ok(mapper().createObjectNode()).build();
214 }
215
216 /**
217 * Purges the flow rules installed by openstacknetworking.
218 *
219 * @return 200 OK with purge result, 404 not found
220 */
221 @GET
222 @Produces(MediaType.APPLICATION_JSON)
223 @Path("purge/rules")
224 public Response purgeRules() {
225
Jian Li167f0c42018-11-18 11:03:51 +0900226 if (purgeRulesBase()) {
227 return ok(mapper().createObjectNode()).build();
228 } else {
229 return Response.serverError().build();
230 }
Jian Li7f024de2018-07-07 03:51:02 +0900231 }
232
233 /**
234 * Configures the ARP mode (proxy | broadcast).
235 *
236 * @param arpmode ARP mode
237 * @return 200 OK with config result, 404 not found
238 */
239 @GET
240 @Produces(MediaType.APPLICATION_JSON)
241 @Path("config/arpmode/{arpmode}")
242 public Response configArpMode(@PathParam("arpmode") String arpmode) {
243
244 String arpModeStr = nullIsIllegal(arpmode, ARP_MODE_REQUIRED);
245 if (checkArpMode(arpModeStr)) {
246 configArpModeBase(arpModeStr);
247
248 ComponentConfigService service = get(ComponentConfigService.class);
249 String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
250 String routingComponent = OpenstackRoutingArpHandler.class.getName();
251
252 // we check the arpMode configured in each component, and purge and
253 // reinstall all rules only if the arpMode is changed to the configured one
254 while (true) {
255 String switchingValue =
256 getPropertyValue(service.getProperties(switchingComponent), ARP_MODE_NAME);
257 String routingValue =
258 getPropertyValue(service.getProperties(routingComponent), ARP_MODE_NAME);
259
260 if (arpModeStr.equals(switchingValue) && arpModeStr.equals(routingValue)) {
261 break;
262 }
263 }
264
265 purgeRulesBase();
266 syncRulesBase();
267 } else {
268 throw new IllegalArgumentException("The ARP mode is not valid");
Jian Li0b564282018-06-20 00:50:53 +0900269 }
Jian Li0b564282018-06-20 00:50:53 +0900270
271 return ok(mapper().createObjectNode()).build();
272 }
Jian Li803a1d52018-06-21 21:47:48 +0900273
274 /**
Jian Licad36c72018-09-13 17:44:54 +0900275 * Configures the security group (enable | disable).
276 *
277 * @param securityGroup security group activation flag
278 * @return 200 OK with config result, 404 not found
279 */
280 @GET
281 @Produces(MediaType.APPLICATION_JSON)
282 @Path("config/securityGroup/{securityGroup}")
283 public Response configSecurityGroup(@PathParam("securityGroup") String securityGroup) {
284 String securityGroupStr = nullIsIllegal(securityGroup, SECURITY_GROUP_FLAG_REQUIRED);
285
286 boolean flag = checkActivationFlag(securityGroupStr);
287
288 ComponentConfigService service = get(ComponentConfigService.class);
289 String securityGroupComponent = OpenstackSecurityGroupHandler.class.getName();
290
291 service.setProperty(securityGroupComponent, USE_SECURITY_GROUP_NAME, String.valueOf(flag));
292
293 return ok(mapper().createObjectNode()).build();
294 }
295
296 /**
Jian Li803a1d52018-06-21 21:47:48 +0900297 * Obtains a collection of all floating IPs.
298 *
299 * @return 200 OK with a collection of floating IPs, 404 not found
300 */
301 @GET
302 @Produces(MediaType.APPLICATION_JSON)
303 @Path("floatingips/all")
304 public Response allFloatingIps() {
305
306 List<NetFloatingIP> floatingIps =
307 Lists.newArrayList(osRouterAdminService.floatingIps());
308 floatingIps.stream()
309 .sorted(Comparator.comparing(NetFloatingIP::getFloatingIpAddress))
310 .forEach(fip -> floatingipsNode.add(fip.getFloatingIpAddress()));
311
312 return ok(root).build();
313 }
314
315 /**
316 * Obtains a collection of all floating IPs mapped with fixed IPs.
317 *
318 * @return 200 OK with a collection of floating IPs mapped with fixed IPs,
319 * 404 not found
320 */
321 @GET
322 @Produces(MediaType.APPLICATION_JSON)
323 @Path("floatingips/mapped")
324 public Response mappedFloatingIps() {
325
326 List<NetFloatingIP> floatingIps =
327 Lists.newArrayList(osRouterAdminService.floatingIps());
328
329 floatingIps.stream()
330 .filter(fip -> !Strings.isNullOrEmpty(fip.getFixedIpAddress()))
331 .sorted(Comparator.comparing(NetFloatingIP::getFloatingIpAddress))
332 .forEach(fip -> floatingipsNode.add(fip.getFloatingIpAddress()));
333
334 return ok(root).build();
335 }
Jian Li7f024de2018-07-07 03:51:02 +0900336
337 private void syncRulesBase() {
Jian Li5c777c62018-11-11 00:31:20 +0900338 // we first initialize the COMPUTE node, in order to feed all instance ports
339 // by referring to ports' information obtained from neutron server
340 osNodeAdminService.completeNodes(COMPUTE).forEach(this::syncRulesBaseForNode);
341 osNodeAdminService.completeNodes(GATEWAY).forEach(this::syncRulesBaseForNode);
342 }
Jian Li88ae51e2018-07-10 02:23:35 +0900343
Jian Li5c777c62018-11-11 00:31:20 +0900344 private void syncRulesBaseForNode(OpenstackNode osNode) {
345 OpenstackNode updated = osNode.updateState(NodeState.INIT);
346 osNodeAdminService.updateNode(updated);
Jian Li88ae51e2018-07-10 02:23:35 +0900347
Jian Li5c777c62018-11-11 00:31:20 +0900348 try {
349 sleep(SLEEP_MS);
350 } catch (InterruptedException e) {
351 log.error("Exception caused during node synchronization...");
352 }
353
354 if (osNodeAdminService.node(osNode.hostname()).state() == NodeState.COMPLETE) {
355 log.info("Finished sync rules for node {}", osNode.hostname());
356 } else {
357 log.info("Failed to sync rules for node {}", osNode.hostname());
358 }
Jian Li7f024de2018-07-07 03:51:02 +0900359 }
360
Jian Li167f0c42018-11-18 11:03:51 +0900361 private boolean purgeRulesBase() {
Jian Li7f024de2018-07-07 03:51:02 +0900362 ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
363 if (appId == null) {
364 throw new ItemNotFoundException("application not found");
365 }
Jian Li167f0c42018-11-18 11:03:51 +0900366
Jian Li7f024de2018-07-07 03:51:02 +0900367 flowRuleService.removeFlowRulesById(appId);
Jian Li167f0c42018-11-18 11:03:51 +0900368
369 boolean result = true;
370 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
371
372 // we make sure all flow rules are removed from the store
373 while (stream(flowRuleService.getFlowEntriesById(appId)
374 .spliterator(), false).count() > 0) {
375
376 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
377
378 try {
379 sleep(SLEEP_MS);
380 } catch (InterruptedException e) {
381 log.error("Exception caused during rule purging...");
382 }
383
384 if (stream(flowRuleService.getFlowEntriesById(appId)
385 .spliterator(), false).count() == 0) {
386 break;
387 } else {
388 flowRuleService.removeFlowRulesById(appId);
389 log.info("Failed to purging flow rules, retrying rule purging...");
390 }
391
392 if (waitMs <= 0) {
393 result = false;
394 break;
395 }
396 }
397
398 if (result) {
399 log.info("Successfully purged flow rules!");
400 } else {
401 log.warn("Failed to purge flow rules.");
402 }
403
404 return result;
Jian Li7f024de2018-07-07 03:51:02 +0900405 }
406
407 private void configArpModeBase(String arpMode) {
408 ComponentConfigService service = get(ComponentConfigService.class);
409 String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
410 String routingComponent = OpenstackRoutingArpHandler.class.getName();
411
412 service.setProperty(switchingComponent, ARP_MODE_NAME, arpMode);
413 service.setProperty(routingComponent, ARP_MODE_NAME, arpMode);
414 }
Jian Li0b564282018-06-20 00:50:53 +0900415}