blob: 7be2074fee95f1a5f00195f25eebb4ac4e38307b [file] [log] [blame]
Hyunsun Moon0d457362017-06-27 17:19:41 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon0d457362017-06-27 17:19:41 +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.openstacknode.impl;
17
Ray Milkey86ad7bb2018-09-27 12:32:28 -070018import com.google.common.collect.Lists;
Hyunsun Moon0d457362017-06-27 17:19:41 +090019import org.onlab.packet.IpAddress;
20import org.onlab.util.Tools;
21import org.onosproject.cfg.ComponentConfigService;
22import org.onosproject.cluster.ClusterService;
23import org.onosproject.cluster.ControllerNode;
24import org.onosproject.cluster.LeadershipService;
25import org.onosproject.cluster.NodeId;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090028import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Port;
31import org.onosproject.net.behaviour.BridgeConfig;
32import org.onosproject.net.behaviour.BridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090033import org.onosproject.net.behaviour.ControllerInfo;
34import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090035import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090036import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon0d457362017-06-27 17:19:41 +090037import org.onosproject.net.behaviour.TunnelDescription;
38import org.onosproject.net.behaviour.TunnelEndPoints;
39import org.onosproject.net.behaviour.TunnelKeys;
40import org.onosproject.net.device.DeviceAdminService;
41import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
43import org.onosproject.net.device.DeviceService;
Daniel Parke2658ba2018-08-24 22:33:29 +090044import org.onosproject.openstacknode.api.DpdkInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090045import org.onosproject.openstacknode.api.NodeState;
46import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090047import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
48import org.onosproject.openstacknode.api.OpenstackNodeEvent;
49import org.onosproject.openstacknode.api.OpenstackNodeHandler;
50import org.onosproject.openstacknode.api.OpenstackNodeListener;
51import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090052import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Daniel Parke2658ba2018-08-24 22:33:29 +090053import org.onosproject.ovsdb.controller.OvsdbClientService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090054import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Parke2658ba2018-08-24 22:33:29 +090055import org.onosproject.ovsdb.controller.OvsdbPort;
56import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
57import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
58import org.onosproject.ovsdb.rfc.table.Interface;
Jian Li51b844c2018-05-31 10:59:03 +090059import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090060import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Modified;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import org.slf4j.Logger;
68
Daniel Parke2658ba2018-08-24 22:33:29 +090069import java.util.Collection;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import java.util.Dictionary;
71import java.util.List;
72import java.util.Objects;
Daniel Parke2658ba2018-08-24 22:33:29 +090073import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090074import java.util.Set;
75import java.util.concurrent.ExecutorService;
76import java.util.stream.Collectors;
77
Hyunsun Moon0d457362017-06-27 17:19:41 +090078import static java.util.concurrent.Executors.newSingleThreadExecutor;
79import static org.onlab.packet.TpPort.tpPort;
80import static org.onlab.util.Tools.groupedThreads;
81import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li5afbea42018-02-28 10:37:03 +090082import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
83import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090084import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090085import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090086import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
87import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
88import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090089import static org.onosproject.openstacknode.api.NodeState.INIT;
90import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090091import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
92import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Ray Milkey8e406512018-10-24 15:56:50 -070093import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
94import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
95import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
96import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +090097import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
98import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +090099import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +0900100import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +0900101import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900102import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103import static org.slf4j.LoggerFactory.getLogger;
104
105/**
106 * Service bootstraps openstack node based on its type.
107 */
Ray Milkey8e406512018-10-24 15:56:50 -0700108@Component(immediate = true,
109 property = {
110 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
111 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
112 }
113)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900114public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
115
Jian Li5afbea42018-02-28 10:37:03 +0900116 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900117
Hyunsun Moon0d457362017-06-27 17:19:41 +0900118 private static final String DEFAULT_OF_PROTO = "tcp";
119 private static final int DEFAULT_OFPORT = 6653;
120 private static final int DPID_BEGIN = 3;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900123 protected CoreService coreService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126 protected LeadershipService leadershipService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 protected ClusterService clusterService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 protected DeviceService deviceService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900135 protected DeviceAdminService deviceAdminService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900138 protected OvsdbController ovsdbController;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900141 protected OpenstackNodeService osNodeService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900144 protected OpenstackNodeAdminService osNodeAdminService;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900147 protected ComponentConfigService componentConfigService;
148
Ray Milkey8e406512018-10-24 15:56:50 -0700149 /** OVSDB server listen port. */
150 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900151
Ray Milkey8e406512018-10-24 15:56:50 -0700152 /** A flag which indicates whether auto-recover openstack node status on switch reconnecting event. */
153 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900154
Hyunsun Moon0d457362017-06-27 17:19:41 +0900155 private final ExecutorService eventExecutor = newSingleThreadExecutor(
156 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
157
158 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
159 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900160 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
161
162 private ApplicationId appId;
163 private NodeId localNode;
164
165 @Activate
166 protected void activate() {
167 appId = coreService.getAppId(APP_ID);
168 localNode = clusterService.getLocalNode().id();
169
170 componentConfigService.registerProperties(getClass());
171 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900172 deviceService.addListener(ovsdbListener);
173 deviceService.addListener(bridgeListener);
174 osNodeService.addListener(osNodeListener);
175
176 log.info("Started");
177 }
178
179 @Deactivate
180 protected void deactivate() {
181 osNodeService.removeListener(osNodeListener);
182 deviceService.removeListener(bridgeListener);
183 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900184 componentConfigService.unregisterProperties(getClass(), false);
185 leadershipService.withdraw(appId.name());
186 eventExecutor.shutdown();
187
188 log.info("Stopped");
189 }
190
191 @Modified
192 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900193 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900194
195 log.info("Modified");
196 }
197
198 @Override
199 public void processInitState(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700200 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
201 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900202 return;
203 }
204 if (!deviceService.isAvailable(osNode.intgBridge())) {
205 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
206 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900207 if (hasDpdkTunnelBridge(osNode)) {
208 createDpdkTunnelBridge(osNode);
209 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900210 }
211
212 @Override
213 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900214 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700215 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
216 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
daniel parkb18424c2018-02-05 15:43:43 +0900217 return;
218 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900219
daniel parkb18424c2018-02-05 15:43:43 +0900220 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900221 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900222 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900223 }
224
225 if (osNode.dataIp() != null &&
226 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
227 createTunnelInterface(osNode);
228 }
229
Daniel Parke2658ba2018-08-24 22:33:29 +0900230 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900231 osNode.dpdkConfig().dpdkIntfs().stream()
232 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(TUNNEL_BRIDGE))
233 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
Ray Milkey8e406512018-10-24 15:56:50 -0700234 osNode, dpdkInterface, ovsdbPortNum, ovsdbController, true));
Daniel Park5a6a7102018-09-06 23:58:33 +0900235
236 osNode.dpdkConfig().dpdkIntfs().stream()
237 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(INTEGRATION_BRIDGE))
238 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
Ray Milkey8e406512018-10-24 15:56:50 -0700239 osNode, dpdkInterface, ovsdbPortNum, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900240 }
241
Jian Lie6312162018-03-21 21:41:00 +0900242 osNode.phyIntfs().forEach(i -> {
243 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900244 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900245 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900246 }
247 });
248
Daniel Park5a6a7102018-09-06 23:58:33 +0900249 if (osNode.vlanIntf() != null &&
250 !isIntfEnabled(osNode, osNode.vlanIntf())) {
251 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
252 osNode.vlanIntf(), deviceService, true);
253 }
daniel parkb18424c2018-02-05 15:43:43 +0900254 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900255 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900256 }
257 }
258
259 @Override
260 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900261 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900262 }
263
264 @Override
265 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900266 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900267 }
268
Daniel Parke2658ba2018-08-24 22:33:29 +0900269 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
270 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
271 return osNode.dpdkConfig().dpdkIntfs().stream()
272 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
273 }
274 return false;
275 }
276
277 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
278
Ray Milkey8e406512018-10-24 15:56:50 -0700279 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900280 if (client == null) {
281 log.info("Failed to get ovsdb client");
282 return false;
283 }
284
285 return client.getBridges().stream()
286 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
287 }
288
Jian Li340165f2018-02-27 10:38:17 +0900289 /**
Jian Li340165f2018-02-27 10:38:17 +0900290 * Creates a bridge with a given name on a given openstack node.
291 *
292 * @param osNode openstack node
293 * @param bridgeName bridge name
294 * @param deviceId device identifier
295 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900296 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
297 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900298
Jian Li789fadb2018-07-10 13:59:47 +0900299 List<ControllerInfo> controllers;
300
301 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
302 controllers = (List<ControllerInfo>) osNode.controllers();
303 } else {
304 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900305 .map(ControllerNode::ip)
306 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900307
Jian Li789fadb2018-07-10 13:59:47 +0900308 controllers = controllerIps.stream()
309 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
310 .collect(Collectors.toList());
311 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900312
313 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900314
Daniel Park92abf312018-08-08 17:01:35 +0900315 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900316 .name(bridgeName)
317 .failMode(BridgeDescription.FailMode.SECURE)
318 .datapathId(dpid)
319 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900320 .controllers(controllers);
321
322 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900323 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900324 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900325
326 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900327 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900328 }
329
Daniel Parke2658ba2018-08-24 22:33:29 +0900330 private void createDpdkTunnelBridge(OpenstackNode osNode) {
331 Device device = deviceService.getDevice(osNode.ovsdb());
332
333 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
334 .name(TUNNEL_BRIDGE)
335 .datapathType(NETDEV.name().toLowerCase());
336
337 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
338 bridgeConfig.addBridge(builder.build());
339 }
340
Jian Li340165f2018-02-27 10:38:17 +0900341 /**
Jian Li340165f2018-02-27 10:38:17 +0900342 * Creates a tunnel interface in a given openstack node.
343 *
344 * @param osNode openstack node
345 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900346 private void createTunnelInterface(OpenstackNode osNode) {
347 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
348 return;
349 }
350
351 Device device = deviceService.getDevice(osNode.ovsdb());
352 if (device == null || !device.is(InterfaceConfig.class)) {
353 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
354 return;
355 }
356
357 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
358 .deviceId(INTEGRATION_BRIDGE)
359 .ifaceName(DEFAULT_TUNNEL)
360 .type(TunnelDescription.Type.VXLAN)
361 .remote(TunnelEndPoints.flowTunnelEndpoint())
362 .key(TunnelKeys.flowTunnelKey())
363 .build();
364
365 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
366 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
367 }
368
Jian Li340165f2018-02-27 10:38:17 +0900369 /**
370 * Checks whether a given network interface in a given openstack node is enabled or not.
371 *
372 * @param osNode openstack node
373 * @param intf network interface name
374 * @return true if the given interface is enabled, false otherwise
375 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900376 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900377 return deviceService.isAvailable(osNode.intgBridge()) &&
378 deviceService.getPorts(osNode.intgBridge()).stream()
379 .anyMatch(port -> Objects.equals(
380 port.annotations().value(PORT_NAME), intf) &&
381 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900382 }
383
Jian Li340165f2018-02-27 10:38:17 +0900384 /**
385 * Checks whether all requirements for this state are fulfilled or not.
386 *
387 * @param osNode openstack node
388 * @return true if all requirements are fulfilled, false otherwise
389 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900390 private boolean isCurrentStateDone(OpenstackNode osNode) {
391 switch (osNode.state()) {
392 case INIT:
Ray Milkey8e406512018-10-24 15:56:50 -0700393 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900394 return false;
395 }
396
Daniel Parke2658ba2018-08-24 22:33:29 +0900397 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
398 if (hasDpdkTunnelBridge(osNode)) {
399 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
400 }
401 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900402 case DEVICE_CREATED:
403 if (osNode.dataIp() != null &&
404 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
405 return false;
406 }
407 if (osNode.vlanIntf() != null &&
408 !isIntfEnabled(osNode, osNode.vlanIntf())) {
409 return false;
410 }
daniel parkb18424c2018-02-05 15:43:43 +0900411 if (osNode.type() == GATEWAY &&
412 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900413 return false;
414 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900415 if (osNode.dpdkConfig() != null &&
416 osNode.dpdkConfig().dpdkIntfs() != null &&
417 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
418 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900419 }
Jian Lie6312162018-03-21 21:41:00 +0900420
421 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
422 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
423 return false;
424 }
425 }
426
Hyunsun Moon0d457362017-06-27 17:19:41 +0900427 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900428 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900429 case INCOMPLETE:
430 // always return false
431 // run init CLI to re-trigger node bootstrap
432 return false;
433 default:
434 return true;
435 }
436 }
437
Jian Li40888bf2018-11-21 09:46:32 +0900438 private boolean isDpdkIntfsCreated(OpenstackNode osNode,
439 Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700440 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900441 if (client == null) {
442 log.info("Failed to get ovsdb client");
443 return false;
444 }
445
446 Set<OvsdbPort> ports = client.getPorts();
447
448 for (DpdkInterface dpdkInterface : dpdkInterfaces) {
449 Optional<OvsdbPort> port = ports.stream()
450 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkInterface.intf()))
451 .findAny();
452
453 if (!port.isPresent()) {
454 return false;
455 }
456 Interface intf = client.getInterface(dpdkInterface.intf());
457 if (intf == null) {
458 return false;
459 }
460
461 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
462 if (mtu == null) {
463 return false;
464 }
465
466 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
467 if (option == null) {
468 return false;
469 }
470
471 if (!mtu.set().contains(dpdkInterface.mtu().intValue()) ||
472 !option.toString().contains(dpdkInterface.pciAddress())) {
Jian Li40888bf2018-11-21 09:46:32 +0900473 log.trace("The dpdk interface {} was created but mtu or " +
474 "pci address is different from the config.");
Daniel Parke2658ba2018-08-24 22:33:29 +0900475 return false;
476 }
477 }
478 return true;
479 }
480
Jian Li340165f2018-02-27 10:38:17 +0900481 /**
482 * Configures the openstack node with new state.
483 *
484 * @param osNode openstack node
485 * @param newState a new state
486 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900487 private void setState(OpenstackNode osNode, NodeState newState) {
488 if (osNode.state() == newState) {
489 return;
490 }
491 OpenstackNode updated = osNode.updateState(newState);
492 osNodeAdminService.updateNode(updated);
493 log.info("Changed {} state: {}", osNode.hostname(), newState);
494 }
495
Jian Li340165f2018-02-27 10:38:17 +0900496 /**
497 * Bootstraps a new openstack node.
498 *
499 * @param osNode openstack node
500 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900501 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900502 if (osNode.type() == CONTROLLER) {
503 if (osNode.state() == INIT && checkEndpoint(osNode)) {
504 setState(osNode, COMPLETE);
505 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900506 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900507 if (isCurrentStateDone(osNode)) {
508 setState(osNode, osNode.state().nextState());
509 } else {
Jian Li97482c12018-07-03 01:08:23 +0900510 log.trace("Processing {} state for {}", osNode.state(),
511 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900512 osNode.state().process(this, osNode);
513 }
514 }
515 }
516
Daniel Park5a6a7102018-09-06 23:58:33 +0900517 private void removeVlanInterface(OpenstackNode osNode) {
518 if (osNode.vlanIntf() != null) {
519 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
520
521 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkInterface);
522 }
523 }
524
525 private void removePhysicalInterface(OpenstackNode osNode) {
526 osNode.phyIntfs().forEach(phyIntf -> {
527 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
528
529 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkInterface);
530 });
531 }
532
533 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode, String intf) {
534 return osNode.dpdkConfig() == null ? Optional.empty() :
535 osNode.dpdkConfig().dpdkIntfs().stream()
536 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
537 .findAny();
538 }
539
540 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
541 String intfName,
542 Optional<DpdkInterface> dpdkInterface) {
543 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700544 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900545 ovsdbController, false);
546 } else {
547 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
548 false);
549 }
550 }
551
552 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700553 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Park489645c2018-10-24 11:34:22 +0900554 if (client == null) {
555 log.info("Failed to get ovsdb client");
556 return;
557 }
558
Daniel Park5a6a7102018-09-06 23:58:33 +0900559 //delete physical interfaces from the node
560 removePhysicalInterface(osNode);
561
562 //delete vlan interface from the node
563 removeVlanInterface(osNode);
564
565 //delete dpdk interfaces from the node
566 if (osNode.dpdkConfig() != null) {
567 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
568 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
Jian Li40888bf2018-11-21 09:46:32 +0900569 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum,
570 ovsdbController, false);
Daniel Park5a6a7102018-09-06 23:58:33 +0900571 }
572 });
573 }
Daniel Park489645c2018-10-24 11:34:22 +0900574
575 //delete tunnel bridge from the node
576 if (hasDpdkTunnelBridge(osNode)) {
577 client.dropBridge(TUNNEL_BRIDGE);
578 }
579
580 //delete integration bridge from the node
581 client.dropBridge(INTEGRATION_BRIDGE);
582
583 //disconnect ovsdb
584 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900585 }
586
Jian Li51b844c2018-05-31 10:59:03 +0900587 /**
588 * Checks the validity of the given endpoint.
589 *
590 * @param osNode gateway node
591 * @return validity result
592 */
593 private boolean checkEndpoint(OpenstackNode osNode) {
594 if (osNode == null) {
595 log.warn("Keystone auth info has not been configured. " +
596 "Please specify auth info via network-cfg.json.");
597 return false;
598 }
599
600 OSClient client = getConnectedClient(osNode);
601
602 if (client == null) {
603 return false;
604 } else {
605 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900606 }
607 }
608
Jian Li340165f2018-02-27 10:38:17 +0900609 /**
Jian Li97482c12018-07-03 01:08:23 +0900610 * Extracts properties from the component configuration context.
611 *
612 * @param context the component context
613 */
614 private void readComponentConfiguration(ComponentContext context) {
615 Dictionary<?, ?> properties = context.getProperties();
616
617 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
618 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700619 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
620 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900621 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700622 ovsdbPortNum = ovsdbPortConfigured;
623 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900624 }
625
626 Boolean autoRecoveryConfigured =
627 getBooleanProperty(properties, AUTO_RECOVERY);
628 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700629 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900630 log.info("Auto recovery flag is NOT " +
631 "configured, default value is {}", autoRecovery);
632 } else {
633 autoRecovery = autoRecoveryConfigured;
634 log.info("Configured. Auto recovery flag is {}", autoRecovery);
635 }
636 }
637
638 /**
Jian Li340165f2018-02-27 10:38:17 +0900639 * An internal OVSDB listener. This listener is used for listening the
640 * network facing events from OVSDB device. If a new OVSDB device is detected,
641 * ONOS tries to bootstrap the openstack node.
642 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900643 private class InternalOvsdbListener implements DeviceListener {
644
645 @Override
646 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900647 return event.subject().type() == Device.Type.CONTROLLER;
648 }
649
650 private boolean isRelevantHelper() {
651 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900652 }
653
654 @Override
655 public void event(DeviceEvent event) {
656 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900657
658 switch (event.type()) {
659 case DEVICE_AVAILABILITY_CHANGED:
660 case DEVICE_ADDED:
661 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900662
663 if (!isRelevantHelper()) {
664 return;
665 }
666
Jian Licab41762018-11-06 12:30:08 +0900667 OpenstackNode osNode = osNodeService.node(device.id());
668
669 if (osNode == null || osNode.type() == CONTROLLER) {
670 return;
671 }
672
Hyunsun Moon0d457362017-06-27 17:19:41 +0900673 if (deviceService.isAvailable(device.id())) {
674 log.debug("OVSDB {} detected", device.id());
675 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900676 }
677 });
678 break;
679 case PORT_ADDED:
680 case PORT_REMOVED:
681 case DEVICE_REMOVED:
682 default:
683 // do nothing
684 break;
685 }
686 }
687 }
688
Jian Li340165f2018-02-27 10:38:17 +0900689 /**
690 * An internal integration bridge listener. This listener is used for
691 * listening the events from integration bridge. To listen the events from
692 * other types of bridge such as provider bridge or tunnel bridge, we need
693 * to augment OpenstackNodeService.node() method.
694 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900695 private class InternalBridgeListener implements DeviceListener {
696
697 @Override
698 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900699 return event.subject().type() == Device.Type.SWITCH;
700 }
701
702 private boolean isRelevantHelper() {
703 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900704 }
705
706 @Override
707 public void event(DeviceEvent event) {
708 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900709
710 switch (event.type()) {
711 case DEVICE_AVAILABILITY_CHANGED:
712 case DEVICE_ADDED:
713 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900714
715 if (!isRelevantHelper()) {
716 return;
717 }
718
Jian Licab41762018-11-06 12:30:08 +0900719 OpenstackNode osNode = osNodeService.node(device.id());
720
721 if (osNode == null || osNode.type() == CONTROLLER) {
722 return;
723 }
724
Hyunsun Moon0d457362017-06-27 17:19:41 +0900725 if (deviceService.isAvailable(device.id())) {
726 log.debug("Integration bridge created on {}", osNode.hostname());
727 bootstrapNode(osNode);
728 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900729 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900730 setState(osNode, INCOMPLETE);
731 }
Jian Li97482c12018-07-03 01:08:23 +0900732
733 if (autoRecovery) {
734 if (osNode.state() == INCOMPLETE ||
735 osNode.state() == DEVICE_CREATED) {
736 log.info("Device {} is reconnected", device.id());
737 osNodeAdminService.updateNode(
738 osNode.updateState(NodeState.INIT));
739 }
740 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900741 });
742 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900743 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900744 case PORT_ADDED:
745 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900746
747 if (!isRelevantHelper()) {
748 return;
749 }
750
Jian Licab41762018-11-06 12:30:08 +0900751 OpenstackNode osNode = osNodeService.node(device.id());
752
753 if (osNode == null || osNode.type() == CONTROLLER) {
754 return;
755 }
756
Hyunsun Moon0d457362017-06-27 17:19:41 +0900757 Port port = event.port();
758 String portName = port.annotations().value(PORT_NAME);
759 if (osNode.state() == DEVICE_CREATED && (
760 Objects.equals(portName, DEFAULT_TUNNEL) ||
761 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900762 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900763 containsPhyIntf(osNode, portName)) ||
764 containsDpdkIntfs(osNode, portName)) {
765 log.info("Interface {} added or updated to {}",
766 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900767 bootstrapNode(osNode);
768 }
769 });
770 break;
771 case PORT_REMOVED:
772 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900773
774 if (!isRelevantHelper()) {
775 return;
776 }
777
Jian Licab41762018-11-06 12:30:08 +0900778 OpenstackNode osNode = osNodeService.node(device.id());
779
780 if (osNode == null || osNode.type() == CONTROLLER) {
781 return;
782 }
783
Hyunsun Moon0d457362017-06-27 17:19:41 +0900784 Port port = event.port();
785 String portName = port.annotations().value(PORT_NAME);
786 if (osNode.state() == COMPLETE && (
787 Objects.equals(portName, DEFAULT_TUNNEL) ||
788 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900789 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900790 containsPhyIntf(osNode, portName)) ||
791 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900792 log.warn("Interface {} removed from {}",
793 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900794 setState(osNode, INCOMPLETE);
795 }
796 });
797 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900798 case DEVICE_REMOVED:
799 default:
800 // do nothing
801 break;
802 }
803 }
804 }
805
Jian Li340165f2018-02-27 10:38:17 +0900806 /**
Jian Lie6312162018-03-21 21:41:00 +0900807 * Checks whether the openstack node contains the given physical interface.
808 *
809 * @param osNode openstack node
810 * @param portName physical interface
811 * @return true if openstack node contains the given physical interface,
812 * false otherwise
813 */
814 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900815 return osNode.phyIntfs().stream()
816 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
817 }
Jian Lie6312162018-03-21 21:41:00 +0900818
Daniel Park5a6a7102018-09-06 23:58:33 +0900819 /**
820 * Checks whether the openstack node contains the given dpdk interface.
821 *
822 * @param osNode openstack node
823 * @param portName dpdk interface
824 * @return true if openstack node contains the given dpdk interface,
825 * false otherwise
826 */
827 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
828 if (osNode.dpdkConfig() == null) {
829 return false;
830 }
831 return osNode.dpdkConfig().dpdkIntfs().stream()
832 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900833 }
834
835 /**
Jian Li340165f2018-02-27 10:38:17 +0900836 * An internal openstack node listener.
837 * The notification is triggered by OpenstackNodeStore.
838 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900839 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
840
Jian Li40888bf2018-11-21 09:46:32 +0900841 private boolean isRelevantHelper() {
842 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900843 }
844
845 @Override
846 public void event(OpenstackNodeEvent event) {
847 switch (event.type()) {
848 case OPENSTACK_NODE_CREATED:
849 case OPENSTACK_NODE_UPDATED:
Jian Li40888bf2018-11-21 09:46:32 +0900850 eventExecutor.execute(() -> {
851
852 if (!isRelevantHelper()) {
853 return;
854 }
855
856 bootstrapNode(event.subject());
857 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900858 break;
859 case OPENSTACK_NODE_REMOVED:
Jian Li40888bf2018-11-21 09:46:32 +0900860 eventExecutor.execute(() -> {
861
862 if (!isRelevantHelper()) {
863 return;
864 }
865
866 processOpenstackNodeRemoved(event.subject());
867 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900868 break;
Jian Li40888bf2018-11-21 09:46:32 +0900869 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900870 default:
871 break;
872 }
873 }
874 }
875}