blob: 9f3d2203882762a66344abbffd7cb3cbd857dc71 [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
Daniel Parke2658ba2018-08-24 22:33:29 +0900438 private boolean isDpdkIntfsCreated(OpenstackNode osNode, Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700439 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900440 if (client == null) {
441 log.info("Failed to get ovsdb client");
442 return false;
443 }
444
445 Set<OvsdbPort> ports = client.getPorts();
446
447 for (DpdkInterface dpdkInterface : dpdkInterfaces) {
448 Optional<OvsdbPort> port = ports.stream()
449 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkInterface.intf()))
450 .findAny();
451
452 if (!port.isPresent()) {
453 return false;
454 }
455 Interface intf = client.getInterface(dpdkInterface.intf());
456 if (intf == null) {
457 return false;
458 }
459
460 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
461 if (mtu == null) {
462 return false;
463 }
464
465 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
466 if (option == null) {
467 return false;
468 }
469
470 if (!mtu.set().contains(dpdkInterface.mtu().intValue()) ||
471 !option.toString().contains(dpdkInterface.pciAddress())) {
472 log.trace("The dpdk interface {} was created but mtu or pci address is different from the config.");
473 return false;
474 }
475 }
476 return true;
477 }
478
Jian Li340165f2018-02-27 10:38:17 +0900479 /**
480 * Configures the openstack node with new state.
481 *
482 * @param osNode openstack node
483 * @param newState a new state
484 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900485 private void setState(OpenstackNode osNode, NodeState newState) {
486 if (osNode.state() == newState) {
487 return;
488 }
489 OpenstackNode updated = osNode.updateState(newState);
490 osNodeAdminService.updateNode(updated);
491 log.info("Changed {} state: {}", osNode.hostname(), newState);
492 }
493
Jian Li340165f2018-02-27 10:38:17 +0900494 /**
495 * Bootstraps a new openstack node.
496 *
497 * @param osNode openstack node
498 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900499 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900500 if (osNode.type() == CONTROLLER) {
501 if (osNode.state() == INIT && checkEndpoint(osNode)) {
502 setState(osNode, COMPLETE);
503 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900504 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900505 if (isCurrentStateDone(osNode)) {
506 setState(osNode, osNode.state().nextState());
507 } else {
Jian Li97482c12018-07-03 01:08:23 +0900508 log.trace("Processing {} state for {}", osNode.state(),
509 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900510 osNode.state().process(this, osNode);
511 }
512 }
513 }
514
Daniel Park5a6a7102018-09-06 23:58:33 +0900515 private void removeVlanInterface(OpenstackNode osNode) {
516 if (osNode.vlanIntf() != null) {
517 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
518
519 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkInterface);
520 }
521 }
522
523 private void removePhysicalInterface(OpenstackNode osNode) {
524 osNode.phyIntfs().forEach(phyIntf -> {
525 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
526
527 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkInterface);
528 });
529 }
530
531 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode, String intf) {
532 return osNode.dpdkConfig() == null ? Optional.empty() :
533 osNode.dpdkConfig().dpdkIntfs().stream()
534 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
535 .findAny();
536 }
537
538 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
539 String intfName,
540 Optional<DpdkInterface> dpdkInterface) {
541 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700542 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900543 ovsdbController, false);
544 } else {
545 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
546 false);
547 }
548 }
549
550 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700551 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Park489645c2018-10-24 11:34:22 +0900552 if (client == null) {
553 log.info("Failed to get ovsdb client");
554 return;
555 }
556
Daniel Park5a6a7102018-09-06 23:58:33 +0900557 //delete physical interfaces from the node
558 removePhysicalInterface(osNode);
559
560 //delete vlan interface from the node
561 removeVlanInterface(osNode);
562
563 //delete dpdk interfaces from the node
564 if (osNode.dpdkConfig() != null) {
565 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
566 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
Ray Milkey8e406512018-10-24 15:56:50 -0700567 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum, ovsdbController, false);
Daniel Park5a6a7102018-09-06 23:58:33 +0900568 }
569 });
570 }
Daniel Park489645c2018-10-24 11:34:22 +0900571
572 //delete tunnel bridge from the node
573 if (hasDpdkTunnelBridge(osNode)) {
574 client.dropBridge(TUNNEL_BRIDGE);
575 }
576
577 //delete integration bridge from the node
578 client.dropBridge(INTEGRATION_BRIDGE);
579
580 //disconnect ovsdb
581 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900582 }
583
Jian Li51b844c2018-05-31 10:59:03 +0900584 /**
585 * Checks the validity of the given endpoint.
586 *
587 * @param osNode gateway node
588 * @return validity result
589 */
590 private boolean checkEndpoint(OpenstackNode osNode) {
591 if (osNode == null) {
592 log.warn("Keystone auth info has not been configured. " +
593 "Please specify auth info via network-cfg.json.");
594 return false;
595 }
596
597 OSClient client = getConnectedClient(osNode);
598
599 if (client == null) {
600 return false;
601 } else {
602 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900603 }
604 }
605
Jian Li340165f2018-02-27 10:38:17 +0900606 /**
Jian Li97482c12018-07-03 01:08:23 +0900607 * Extracts properties from the component configuration context.
608 *
609 * @param context the component context
610 */
611 private void readComponentConfiguration(ComponentContext context) {
612 Dictionary<?, ?> properties = context.getProperties();
613
614 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
615 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700616 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
617 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900618 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700619 ovsdbPortNum = ovsdbPortConfigured;
620 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900621 }
622
623 Boolean autoRecoveryConfigured =
624 getBooleanProperty(properties, AUTO_RECOVERY);
625 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700626 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900627 log.info("Auto recovery flag is NOT " +
628 "configured, default value is {}", autoRecovery);
629 } else {
630 autoRecovery = autoRecoveryConfigured;
631 log.info("Configured. Auto recovery flag is {}", autoRecovery);
632 }
633 }
634
635 /**
Jian Li340165f2018-02-27 10:38:17 +0900636 * An internal OVSDB listener. This listener is used for listening the
637 * network facing events from OVSDB device. If a new OVSDB device is detected,
638 * ONOS tries to bootstrap the openstack node.
639 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900640 private class InternalOvsdbListener implements DeviceListener {
641
642 @Override
643 public boolean isRelevant(DeviceEvent event) {
644 NodeId leader = leadershipService.getLeader(appId.name());
645 return Objects.equals(localNode, leader) &&
646 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900647 osNodeService.node(event.subject().id()) != null &&
648 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900649 }
650
651 @Override
652 public void event(DeviceEvent event) {
653 Device device = event.subject();
654 OpenstackNode osNode = osNodeService.node(device.id());
655
656 switch (event.type()) {
657 case DEVICE_AVAILABILITY_CHANGED:
658 case DEVICE_ADDED:
659 eventExecutor.execute(() -> {
660 if (deviceService.isAvailable(device.id())) {
661 log.debug("OVSDB {} detected", device.id());
662 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900663 }
664 });
665 break;
666 case PORT_ADDED:
667 case PORT_REMOVED:
668 case DEVICE_REMOVED:
669 default:
670 // do nothing
671 break;
672 }
673 }
674 }
675
Jian Li340165f2018-02-27 10:38:17 +0900676 /**
677 * An internal integration bridge listener. This listener is used for
678 * listening the events from integration bridge. To listen the events from
679 * other types of bridge such as provider bridge or tunnel bridge, we need
680 * to augment OpenstackNodeService.node() method.
681 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900682 private class InternalBridgeListener implements DeviceListener {
683
684 @Override
685 public boolean isRelevant(DeviceEvent event) {
686 NodeId leader = leadershipService.getLeader(appId.name());
687 return Objects.equals(localNode, leader) &&
688 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900689 osNodeService.node(event.subject().id()) != null &&
690 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900691 }
692
693 @Override
694 public void event(DeviceEvent event) {
695 Device device = event.subject();
696 OpenstackNode osNode = osNodeService.node(device.id());
697
698 switch (event.type()) {
699 case DEVICE_AVAILABILITY_CHANGED:
700 case DEVICE_ADDED:
701 eventExecutor.execute(() -> {
702 if (deviceService.isAvailable(device.id())) {
703 log.debug("Integration bridge created on {}", osNode.hostname());
704 bootstrapNode(osNode);
705 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900706 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900707 setState(osNode, INCOMPLETE);
708 }
Jian Li97482c12018-07-03 01:08:23 +0900709
710 if (autoRecovery) {
711 if (osNode.state() == INCOMPLETE ||
712 osNode.state() == DEVICE_CREATED) {
713 log.info("Device {} is reconnected", device.id());
714 osNodeAdminService.updateNode(
715 osNode.updateState(NodeState.INIT));
716 }
717 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900718 });
719 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900720 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900721 case PORT_ADDED:
722 eventExecutor.execute(() -> {
723 Port port = event.port();
724 String portName = port.annotations().value(PORT_NAME);
725 if (osNode.state() == DEVICE_CREATED && (
726 Objects.equals(portName, DEFAULT_TUNNEL) ||
727 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900728 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900729 containsPhyIntf(osNode, portName)) ||
730 containsDpdkIntfs(osNode, portName)) {
731 log.info("Interface {} added or updated to {}",
732 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900733 bootstrapNode(osNode);
734 }
735 });
736 break;
737 case PORT_REMOVED:
738 eventExecutor.execute(() -> {
739 Port port = event.port();
740 String portName = port.annotations().value(PORT_NAME);
741 if (osNode.state() == COMPLETE && (
742 Objects.equals(portName, DEFAULT_TUNNEL) ||
743 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900744 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900745 containsPhyIntf(osNode, portName)) ||
746 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900747 log.warn("Interface {} removed from {}",
748 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900749 setState(osNode, INCOMPLETE);
750 }
751 });
752 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900753 case DEVICE_REMOVED:
754 default:
755 // do nothing
756 break;
757 }
758 }
759 }
760
Jian Li340165f2018-02-27 10:38:17 +0900761 /**
Jian Lie6312162018-03-21 21:41:00 +0900762 * Checks whether the openstack node contains the given physical interface.
763 *
764 * @param osNode openstack node
765 * @param portName physical interface
766 * @return true if openstack node contains the given physical interface,
767 * false otherwise
768 */
769 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900770 return osNode.phyIntfs().stream()
771 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
772 }
Jian Lie6312162018-03-21 21:41:00 +0900773
Daniel Park5a6a7102018-09-06 23:58:33 +0900774 /**
775 * Checks whether the openstack node contains the given dpdk interface.
776 *
777 * @param osNode openstack node
778 * @param portName dpdk interface
779 * @return true if openstack node contains the given dpdk interface,
780 * false otherwise
781 */
782 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
783 if (osNode.dpdkConfig() == null) {
784 return false;
785 }
786 return osNode.dpdkConfig().dpdkIntfs().stream()
787 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900788 }
789
790 /**
Jian Li340165f2018-02-27 10:38:17 +0900791 * An internal openstack node listener.
792 * The notification is triggered by OpenstackNodeStore.
793 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900794 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
795
796 @Override
797 public boolean isRelevant(OpenstackNodeEvent event) {
798 NodeId leader = leadershipService.getLeader(appId.name());
799 return Objects.equals(localNode, leader);
800 }
801
802 @Override
803 public void event(OpenstackNodeEvent event) {
804 switch (event.type()) {
805 case OPENSTACK_NODE_CREATED:
806 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900807 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900808 break;
809 case OPENSTACK_NODE_COMPLETE:
810 break;
811 case OPENSTACK_NODE_REMOVED:
Daniel Park5a6a7102018-09-06 23:58:33 +0900812 eventExecutor.execute(() -> processOpenstackNodeRemoved(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900813 break;
814 default:
815 break;
816 }
817 }
818 }
819}