blob: c2c3d570c25c5f752777cf8658e54583fab36954 [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 Li621f73c2018-12-15 01:49:22 +090082import static org.onosproject.openstacknode.api.Constants.GENEVE;
83import static org.onosproject.openstacknode.api.Constants.GENEVE_TUNNEL;
SONA Project6bc5c4a2018-12-14 23:49:52 +090084import static org.onosproject.openstacknode.api.Constants.GRE;
Jian Li2d68c192018-12-13 15:52:59 +090085import static org.onosproject.openstacknode.api.Constants.GRE_TUNNEL;
Jian Li5afbea42018-02-28 10:37:03 +090086import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090087import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
SONA Project6bc5c4a2018-12-14 23:49:52 +090088import static org.onosproject.openstacknode.api.Constants.VXLAN;
89import static org.onosproject.openstacknode.api.Constants.VXLAN_TUNNEL;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090090import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090091import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
92import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
93import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090094import static org.onosproject.openstacknode.api.NodeState.INIT;
95import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090096import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
97import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Ray Milkey8e406512018-10-24 15:56:50 -070098import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
99import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
100import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
101import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +0900102import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
103import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +0900104import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +0900105import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +0900106import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900107import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900108import static org.slf4j.LoggerFactory.getLogger;
109
110/**
111 * Service bootstraps openstack node based on its type.
112 */
Ray Milkey8e406512018-10-24 15:56:50 -0700113@Component(immediate = true,
114 property = {
115 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
116 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
117 }
118)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900119public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
120
Jian Li5afbea42018-02-28 10:37:03 +0900121 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900122
Hyunsun Moon0d457362017-06-27 17:19:41 +0900123 private static final String DEFAULT_OF_PROTO = "tcp";
124 private static final int DEFAULT_OFPORT = 6653;
125 private static final int DPID_BEGIN = 3;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900128 protected CoreService coreService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900131 protected LeadershipService leadershipService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900134 protected ClusterService clusterService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900137 protected DeviceService deviceService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900140 protected DeviceAdminService deviceAdminService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900143 protected OvsdbController ovsdbController;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900146 protected OpenstackNodeService osNodeService;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900149 protected OpenstackNodeAdminService osNodeAdminService;
150
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900152 protected ComponentConfigService componentConfigService;
153
Ray Milkey8e406512018-10-24 15:56:50 -0700154 /** OVSDB server listen port. */
155 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900156
Jian Li900b7232018-12-14 14:04:51 +0900157 /** Indicates whether auto-recover openstack node status on switch re-conn event. */
Ray Milkey8e406512018-10-24 15:56:50 -0700158 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900159
Hyunsun Moon0d457362017-06-27 17:19:41 +0900160 private final ExecutorService eventExecutor = newSingleThreadExecutor(
161 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
162
163 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
164 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900165 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
166
167 private ApplicationId appId;
168 private NodeId localNode;
169
170 @Activate
171 protected void activate() {
172 appId = coreService.getAppId(APP_ID);
173 localNode = clusterService.getLocalNode().id();
174
175 componentConfigService.registerProperties(getClass());
176 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900177 deviceService.addListener(ovsdbListener);
178 deviceService.addListener(bridgeListener);
179 osNodeService.addListener(osNodeListener);
180
181 log.info("Started");
182 }
183
184 @Deactivate
185 protected void deactivate() {
186 osNodeService.removeListener(osNodeListener);
187 deviceService.removeListener(bridgeListener);
188 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900189 componentConfigService.unregisterProperties(getClass(), false);
190 leadershipService.withdraw(appId.name());
191 eventExecutor.shutdown();
192
193 log.info("Stopped");
194 }
195
196 @Modified
197 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900198 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900199
200 log.info("Modified");
201 }
202
203 @Override
204 public void processInitState(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700205 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
206 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900207 return;
208 }
209 if (!deviceService.isAvailable(osNode.intgBridge())) {
210 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
211 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900212 if (hasDpdkTunnelBridge(osNode)) {
213 createDpdkTunnelBridge(osNode);
214 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900215 }
216
217 @Override
218 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900219 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700220 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
221 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
daniel parkb18424c2018-02-05 15:43:43 +0900222 return;
223 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900224
daniel parkb18424c2018-02-05 15:43:43 +0900225 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900226 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900227 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900228 }
229
230 if (osNode.dataIp() != null &&
Jian Li2d68c192018-12-13 15:52:59 +0900231 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
232 createVxlanTunnelInterface(osNode);
233 }
234
235 if (osNode.dataIp() != null &&
236 !isIntfEnabled(osNode, GRE_TUNNEL)) {
237 createGreTunnelInterface(osNode);
daniel parkb18424c2018-02-05 15:43:43 +0900238 }
239
Jian Li621f73c2018-12-15 01:49:22 +0900240 if (osNode.dataIp() != null &&
241 !isIntfEnabled(osNode, GENEVE_TUNNEL)) {
242 createGeneveTunnelInterface(osNode);
243 }
244
Daniel Parke2658ba2018-08-24 22:33:29 +0900245 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900246 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900247 .filter(dpdkintf -> dpdkintf.deviceName().equals(TUNNEL_BRIDGE))
248 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
249 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Park5a6a7102018-09-06 23:58:33 +0900250
251 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900252 .filter(dpdkintf -> dpdkintf.deviceName().equals(INTEGRATION_BRIDGE))
253 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
254 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900255 }
256
Jian Lie6312162018-03-21 21:41:00 +0900257 osNode.phyIntfs().forEach(i -> {
258 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900259 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900260 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900261 }
262 });
263
Daniel Park5a6a7102018-09-06 23:58:33 +0900264 if (osNode.vlanIntf() != null &&
265 !isIntfEnabled(osNode, osNode.vlanIntf())) {
266 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900267 osNode.vlanIntf(), deviceService, true);
Daniel Park5a6a7102018-09-06 23:58:33 +0900268 }
daniel parkb18424c2018-02-05 15:43:43 +0900269 } catch (Exception e) {
Jian Li621f73c2018-12-15 01:49:22 +0900270 log.error("Exception occurred because of {}", e);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900271 }
272 }
273
274 @Override
275 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900276 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900277 }
278
279 @Override
280 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900281 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900282 }
283
Daniel Parke2658ba2018-08-24 22:33:29 +0900284 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
285 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
286 return osNode.dpdkConfig().dpdkIntfs().stream()
287 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
288 }
289 return false;
290 }
291
292 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
293
Ray Milkey8e406512018-10-24 15:56:50 -0700294 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900295 if (client == null) {
296 log.info("Failed to get ovsdb client");
297 return false;
298 }
299
300 return client.getBridges().stream()
301 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
302 }
303
Jian Li340165f2018-02-27 10:38:17 +0900304 /**
Jian Li340165f2018-02-27 10:38:17 +0900305 * Creates a bridge with a given name on a given openstack node.
306 *
307 * @param osNode openstack node
308 * @param bridgeName bridge name
309 * @param deviceId device identifier
310 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900311 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
312 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900313
Jian Li789fadb2018-07-10 13:59:47 +0900314 List<ControllerInfo> controllers;
315
316 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
317 controllers = (List<ControllerInfo>) osNode.controllers();
318 } else {
319 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900320 .map(ControllerNode::ip)
321 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900322
Jian Li789fadb2018-07-10 13:59:47 +0900323 controllers = controllerIps.stream()
324 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
325 .collect(Collectors.toList());
326 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900327
328 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900329
Daniel Park92abf312018-08-08 17:01:35 +0900330 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900331 .name(bridgeName)
332 .failMode(BridgeDescription.FailMode.SECURE)
333 .datapathId(dpid)
334 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900335 .controllers(controllers);
336
337 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900338 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900339 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900340
341 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900342 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900343 }
344
Daniel Parke2658ba2018-08-24 22:33:29 +0900345 private void createDpdkTunnelBridge(OpenstackNode osNode) {
346 Device device = deviceService.getDevice(osNode.ovsdb());
347
348 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
349 .name(TUNNEL_BRIDGE)
350 .datapathType(NETDEV.name().toLowerCase());
351
352 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
353 bridgeConfig.addBridge(builder.build());
354 }
355
Jian Li340165f2018-02-27 10:38:17 +0900356 /**
Jian Li2d68c192018-12-13 15:52:59 +0900357 * Creates a VXLAN tunnel interface in a given openstack node.
358 *
359 * @param osNode openstack node
360 */
361 private void createVxlanTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900362 createTunnelInterface(osNode, VXLAN, VXLAN_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900363 }
364
365 /**
366 * Creates a GRE tunnel interface in a given openstack node.
367 *
368 * @param osNode openstack node
369 */
370 private void createGreTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900371 createTunnelInterface(osNode, GRE, GRE_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900372 }
373
374 /**
Jian Li621f73c2018-12-15 01:49:22 +0900375 * Creates a GENEVE tunnel interface in a given openstack node.
376 *
377 * @param osNode openstack node
378 */
379 private void createGeneveTunnelInterface(OpenstackNode osNode) {
380 createTunnelInterface(osNode, GENEVE, GENEVE_TUNNEL);
381 }
382
383 /**
Jian Li340165f2018-02-27 10:38:17 +0900384 * Creates a tunnel interface in a given openstack node.
385 *
386 * @param osNode openstack node
387 */
Jian Li2d68c192018-12-13 15:52:59 +0900388 private void createTunnelInterface(OpenstackNode osNode,
SONA Project6bc5c4a2018-12-14 23:49:52 +0900389 String type, String intfName) {
Jian Li2d68c192018-12-13 15:52:59 +0900390 if (isIntfEnabled(osNode, intfName)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 return;
392 }
393
394 Device device = deviceService.getDevice(osNode.ovsdb());
395 if (device == null || !device.is(InterfaceConfig.class)) {
396 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
397 return;
398 }
399
Jian Li2d68c192018-12-13 15:52:59 +0900400 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900401
402 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Jian Li2d68c192018-12-13 15:52:59 +0900403 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
404 }
405
406 /**
407 * Builds tunnel description according to the network type.
408 *
409 * @param type network type
410 * @return tunnel description
411 */
SONA Project6bc5c4a2018-12-14 23:49:52 +0900412 private TunnelDescription buildTunnelDesc(String type, String intfName) {
Jian Li621f73c2018-12-15 01:49:22 +0900413 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
Jian Li2d68c192018-12-13 15:52:59 +0900414 TunnelDescription.Builder tdBuilder =
415 DefaultTunnelDescription.builder()
416 .deviceId(INTEGRATION_BRIDGE)
417 .ifaceName(intfName)
418 .remote(TunnelEndPoints.flowTunnelEndpoint())
419 .key(TunnelKeys.flowTunnelKey());
420
421 switch (type) {
422 case VXLAN:
423 tdBuilder.type(TunnelDescription.Type.VXLAN);
424 break;
425 case GRE:
426 tdBuilder.type(TunnelDescription.Type.GRE);
427 break;
Jian Li621f73c2018-12-15 01:49:22 +0900428 case GENEVE:
429 tdBuilder.type(TunnelDescription.Type.GENEVE);
430 break;
Jian Li2d68c192018-12-13 15:52:59 +0900431 default:
432 return null;
433 }
434
435 return tdBuilder.build();
436 }
437 return null;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900438 }
439
Jian Li340165f2018-02-27 10:38:17 +0900440 /**
Jian Li5ecfd1a2018-12-10 11:41:03 +0900441 * Checks whether a given network interface in a given openstack node
442 * is enabled or not.
Jian Li340165f2018-02-27 10:38:17 +0900443 *
444 * @param osNode openstack node
445 * @param intf network interface name
446 * @return true if the given interface is enabled, false otherwise
447 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900448 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900449 return deviceService.isAvailable(osNode.intgBridge()) &&
450 deviceService.getPorts(osNode.intgBridge()).stream()
451 .anyMatch(port -> Objects.equals(
452 port.annotations().value(PORT_NAME), intf) &&
453 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900454 }
455
Jian Li340165f2018-02-27 10:38:17 +0900456 /**
457 * Checks whether all requirements for this state are fulfilled or not.
458 *
459 * @param osNode openstack node
460 * @return true if all requirements are fulfilled, false otherwise
461 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900462 private boolean isCurrentStateDone(OpenstackNode osNode) {
463 switch (osNode.state()) {
464 case INIT:
Jian Li5ecfd1a2018-12-10 11:41:03 +0900465 if (!isOvsdbConnected(osNode, ovsdbPortNum,
466 ovsdbController, deviceService)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900467 return false;
468 }
469
Daniel Parke2658ba2018-08-24 22:33:29 +0900470 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
471 if (hasDpdkTunnelBridge(osNode)) {
472 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
473 }
474 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900475 case DEVICE_CREATED:
476 if (osNode.dataIp() != null &&
Jian Li2d68c192018-12-13 15:52:59 +0900477 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
478 return false;
479 }
480 if (osNode.dataIp() != null &&
481 !isIntfEnabled(osNode, GRE_TUNNEL)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900482 return false;
483 }
Jian Li621f73c2018-12-15 01:49:22 +0900484 if (osNode.dataIp() != null &&
485 !isIntfEnabled(osNode, GENEVE_TUNNEL)) {
486 return false;
487 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900488 if (osNode.vlanIntf() != null &&
489 !isIntfEnabled(osNode, osNode.vlanIntf())) {
490 return false;
491 }
daniel parkb18424c2018-02-05 15:43:43 +0900492 if (osNode.type() == GATEWAY &&
493 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900494 return false;
495 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900496 if (osNode.dpdkConfig() != null &&
497 osNode.dpdkConfig().dpdkIntfs() != null &&
498 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
499 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900500 }
Jian Lie6312162018-03-21 21:41:00 +0900501
502 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
503 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
504 return false;
505 }
506 }
507
Hyunsun Moon0d457362017-06-27 17:19:41 +0900508 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900509 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900510 case INCOMPLETE:
511 // always return false
512 // run init CLI to re-trigger node bootstrap
513 return false;
514 default:
515 return true;
516 }
517 }
518
Jian Li40888bf2018-11-21 09:46:32 +0900519 private boolean isDpdkIntfsCreated(OpenstackNode osNode,
520 Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700521 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900522 if (client == null) {
523 log.info("Failed to get ovsdb client");
524 return false;
525 }
526
527 Set<OvsdbPort> ports = client.getPorts();
528
Jian Li5ecfd1a2018-12-10 11:41:03 +0900529 for (DpdkInterface dpdkIntf : dpdkInterfaces) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900530 Optional<OvsdbPort> port = ports.stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900531 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkIntf.intf()))
Daniel Parke2658ba2018-08-24 22:33:29 +0900532 .findAny();
533
534 if (!port.isPresent()) {
535 return false;
536 }
Jian Li5ecfd1a2018-12-10 11:41:03 +0900537 Interface intf = client.getInterface(dpdkIntf.intf());
Daniel Parke2658ba2018-08-24 22:33:29 +0900538 if (intf == null) {
539 return false;
540 }
541
542 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
543 if (mtu == null) {
544 return false;
545 }
546
547 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
548 if (option == null) {
549 return false;
550 }
551
Jian Li5ecfd1a2018-12-10 11:41:03 +0900552 if (!mtu.set().contains(dpdkIntf.mtu().intValue()) ||
553 !option.toString().contains(dpdkIntf.pciAddress())) {
Jian Li40888bf2018-11-21 09:46:32 +0900554 log.trace("The dpdk interface {} was created but mtu or " +
555 "pci address is different from the config.");
Daniel Parke2658ba2018-08-24 22:33:29 +0900556 return false;
557 }
558 }
559 return true;
560 }
561
Jian Li340165f2018-02-27 10:38:17 +0900562 /**
563 * Configures the openstack node with new state.
564 *
565 * @param osNode openstack node
566 * @param newState a new state
567 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900568 private void setState(OpenstackNode osNode, NodeState newState) {
569 if (osNode.state() == newState) {
570 return;
571 }
572 OpenstackNode updated = osNode.updateState(newState);
573 osNodeAdminService.updateNode(updated);
574 log.info("Changed {} state: {}", osNode.hostname(), newState);
575 }
576
Jian Li340165f2018-02-27 10:38:17 +0900577 /**
578 * Bootstraps a new openstack node.
579 *
580 * @param osNode openstack node
581 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900582 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900583 if (osNode.type() == CONTROLLER) {
584 if (osNode.state() == INIT && checkEndpoint(osNode)) {
585 setState(osNode, COMPLETE);
586 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900587 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900588 if (isCurrentStateDone(osNode)) {
589 setState(osNode, osNode.state().nextState());
590 } else {
Jian Li97482c12018-07-03 01:08:23 +0900591 log.trace("Processing {} state for {}", osNode.state(),
592 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900593 osNode.state().process(this, osNode);
594 }
595 }
596 }
597
Daniel Park5a6a7102018-09-06 23:58:33 +0900598 private void removeVlanInterface(OpenstackNode osNode) {
599 if (osNode.vlanIntf() != null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900600 Optional<DpdkInterface> dpdkIntf =
601 dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900602
Jian Li5ecfd1a2018-12-10 11:41:03 +0900603 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900604 }
605 }
606
607 private void removePhysicalInterface(OpenstackNode osNode) {
608 osNode.phyIntfs().forEach(phyIntf -> {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900609 Optional<DpdkInterface> dpdkIntf = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900610
Jian Li5ecfd1a2018-12-10 11:41:03 +0900611 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900612 });
613 }
614
Jian Li5ecfd1a2018-12-10 11:41:03 +0900615 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode,
616 String intf) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900617 return osNode.dpdkConfig() == null ? Optional.empty() :
618 osNode.dpdkConfig().dpdkIntfs().stream()
619 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
620 .findAny();
621 }
622
623 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
624 String intfName,
625 Optional<DpdkInterface> dpdkInterface) {
626 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700627 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900628 ovsdbController, false);
629 } else {
630 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
631 false);
632 }
633 }
634
635 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700636 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Park489645c2018-10-24 11:34:22 +0900637 if (client == null) {
638 log.info("Failed to get ovsdb client");
639 return;
640 }
641
Daniel Park5a6a7102018-09-06 23:58:33 +0900642 //delete physical interfaces from the node
643 removePhysicalInterface(osNode);
644
645 //delete vlan interface from the node
646 removeVlanInterface(osNode);
647
648 //delete dpdk interfaces from the node
649 if (osNode.dpdkConfig() != null) {
650 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
651 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
Jian Li40888bf2018-11-21 09:46:32 +0900652 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum,
653 ovsdbController, false);
Daniel Park5a6a7102018-09-06 23:58:33 +0900654 }
655 });
656 }
Daniel Park489645c2018-10-24 11:34:22 +0900657
658 //delete tunnel bridge from the node
659 if (hasDpdkTunnelBridge(osNode)) {
660 client.dropBridge(TUNNEL_BRIDGE);
661 }
662
663 //delete integration bridge from the node
664 client.dropBridge(INTEGRATION_BRIDGE);
665
666 //disconnect ovsdb
667 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900668 }
669
Jian Li51b844c2018-05-31 10:59:03 +0900670 /**
671 * Checks the validity of the given endpoint.
672 *
673 * @param osNode gateway node
674 * @return validity result
675 */
676 private boolean checkEndpoint(OpenstackNode osNode) {
677 if (osNode == null) {
678 log.warn("Keystone auth info has not been configured. " +
679 "Please specify auth info via network-cfg.json.");
680 return false;
681 }
682
683 OSClient client = getConnectedClient(osNode);
684
685 if (client == null) {
686 return false;
687 } else {
688 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900689 }
690 }
691
Jian Li340165f2018-02-27 10:38:17 +0900692 /**
Jian Li97482c12018-07-03 01:08:23 +0900693 * Extracts properties from the component configuration context.
694 *
695 * @param context the component context
696 */
697 private void readComponentConfiguration(ComponentContext context) {
698 Dictionary<?, ?> properties = context.getProperties();
699
700 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
701 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700702 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
703 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900704 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700705 ovsdbPortNum = ovsdbPortConfigured;
706 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900707 }
708
709 Boolean autoRecoveryConfigured =
710 getBooleanProperty(properties, AUTO_RECOVERY);
711 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700712 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900713 log.info("Auto recovery flag is NOT " +
714 "configured, default value is {}", autoRecovery);
715 } else {
716 autoRecovery = autoRecoveryConfigured;
717 log.info("Configured. Auto recovery flag is {}", autoRecovery);
718 }
719 }
720
721 /**
Jian Li340165f2018-02-27 10:38:17 +0900722 * An internal OVSDB listener. This listener is used for listening the
723 * network facing events from OVSDB device. If a new OVSDB device is detected,
724 * ONOS tries to bootstrap the openstack node.
725 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900726 private class InternalOvsdbListener implements DeviceListener {
727
728 @Override
729 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900730 return event.subject().type() == Device.Type.CONTROLLER;
731 }
732
733 private boolean isRelevantHelper() {
734 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900735 }
736
737 @Override
738 public void event(DeviceEvent event) {
739 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900740
741 switch (event.type()) {
742 case DEVICE_AVAILABILITY_CHANGED:
743 case DEVICE_ADDED:
744 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900745
746 if (!isRelevantHelper()) {
747 return;
748 }
749
Jian Licab41762018-11-06 12:30:08 +0900750 OpenstackNode osNode = osNodeService.node(device.id());
751
752 if (osNode == null || osNode.type() == CONTROLLER) {
753 return;
754 }
755
Hyunsun Moon0d457362017-06-27 17:19:41 +0900756 if (deviceService.isAvailable(device.id())) {
757 log.debug("OVSDB {} detected", device.id());
758 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900759 }
760 });
761 break;
762 case PORT_ADDED:
763 case PORT_REMOVED:
764 case DEVICE_REMOVED:
765 default:
766 // do nothing
767 break;
768 }
769 }
770 }
771
Jian Li340165f2018-02-27 10:38:17 +0900772 /**
773 * An internal integration bridge listener. This listener is used for
774 * listening the events from integration bridge. To listen the events from
775 * other types of bridge such as provider bridge or tunnel bridge, we need
776 * to augment OpenstackNodeService.node() method.
777 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900778 private class InternalBridgeListener implements DeviceListener {
779
780 @Override
781 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900782 return event.subject().type() == Device.Type.SWITCH;
783 }
784
785 private boolean isRelevantHelper() {
786 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900787 }
788
789 @Override
790 public void event(DeviceEvent event) {
791 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900792
793 switch (event.type()) {
794 case DEVICE_AVAILABILITY_CHANGED:
795 case DEVICE_ADDED:
796 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900797
798 if (!isRelevantHelper()) {
799 return;
800 }
801
Jian Licab41762018-11-06 12:30:08 +0900802 OpenstackNode osNode = osNodeService.node(device.id());
803
804 if (osNode == null || osNode.type() == CONTROLLER) {
805 return;
806 }
807
Hyunsun Moon0d457362017-06-27 17:19:41 +0900808 if (deviceService.isAvailable(device.id())) {
809 log.debug("Integration bridge created on {}", osNode.hostname());
810 bootstrapNode(osNode);
811 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900812 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900813 setState(osNode, INCOMPLETE);
814 }
Jian Li97482c12018-07-03 01:08:23 +0900815
816 if (autoRecovery) {
817 if (osNode.state() == INCOMPLETE ||
818 osNode.state() == DEVICE_CREATED) {
819 log.info("Device {} is reconnected", device.id());
820 osNodeAdminService.updateNode(
821 osNode.updateState(NodeState.INIT));
822 }
823 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900824 });
825 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900826 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900827 case PORT_ADDED:
828 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900829
830 if (!isRelevantHelper()) {
831 return;
832 }
833
Jian Licab41762018-11-06 12:30:08 +0900834 OpenstackNode osNode = osNodeService.node(device.id());
835
836 if (osNode == null || osNode.type() == CONTROLLER) {
837 return;
838 }
839
Hyunsun Moon0d457362017-06-27 17:19:41 +0900840 Port port = event.port();
841 String portName = port.annotations().value(PORT_NAME);
842 if (osNode.state() == DEVICE_CREATED && (
Jian Li2d68c192018-12-13 15:52:59 +0900843 Objects.equals(portName, VXLAN_TUNNEL) ||
844 Objects.equals(portName, GRE_TUNNEL) ||
Jian Li621f73c2018-12-15 01:49:22 +0900845 Objects.equals(portName, GENEVE_TUNNEL) ||
Hyunsun Moon0d457362017-06-27 17:19:41 +0900846 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900847 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900848 containsPhyIntf(osNode, portName)) ||
849 containsDpdkIntfs(osNode, portName)) {
850 log.info("Interface {} added or updated to {}",
851 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900852 bootstrapNode(osNode);
853 }
854 });
855 break;
856 case PORT_REMOVED:
857 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900858
859 if (!isRelevantHelper()) {
860 return;
861 }
862
Jian Licab41762018-11-06 12:30:08 +0900863 OpenstackNode osNode = osNodeService.node(device.id());
864
865 if (osNode == null || osNode.type() == CONTROLLER) {
866 return;
867 }
868
Hyunsun Moon0d457362017-06-27 17:19:41 +0900869 Port port = event.port();
870 String portName = port.annotations().value(PORT_NAME);
871 if (osNode.state() == COMPLETE && (
Jian Li2d68c192018-12-13 15:52:59 +0900872 Objects.equals(portName, VXLAN_TUNNEL) ||
873 Objects.equals(portName, GRE_TUNNEL) ||
Jian Li621f73c2018-12-15 01:49:22 +0900874 Objects.equals(portName, GENEVE_TUNNEL) ||
Hyunsun Moon0d457362017-06-27 17:19:41 +0900875 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900876 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900877 containsPhyIntf(osNode, portName)) ||
878 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900879 log.warn("Interface {} removed from {}",
880 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900881 setState(osNode, INCOMPLETE);
882 }
883 });
884 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900885 case DEVICE_REMOVED:
886 default:
887 // do nothing
888 break;
889 }
890 }
891 }
892
Jian Li340165f2018-02-27 10:38:17 +0900893 /**
Jian Lie6312162018-03-21 21:41:00 +0900894 * Checks whether the openstack node contains the given physical interface.
895 *
896 * @param osNode openstack node
897 * @param portName physical interface
898 * @return true if openstack node contains the given physical interface,
899 * false otherwise
900 */
901 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900902 return osNode.phyIntfs().stream()
903 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
904 }
Jian Lie6312162018-03-21 21:41:00 +0900905
Daniel Park5a6a7102018-09-06 23:58:33 +0900906 /**
907 * Checks whether the openstack node contains the given dpdk interface.
908 *
909 * @param osNode openstack node
910 * @param portName dpdk interface
911 * @return true if openstack node contains the given dpdk interface,
912 * false otherwise
913 */
914 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
915 if (osNode.dpdkConfig() == null) {
916 return false;
917 }
918 return osNode.dpdkConfig().dpdkIntfs().stream()
919 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900920 }
921
922 /**
Jian Li340165f2018-02-27 10:38:17 +0900923 * An internal openstack node listener.
924 * The notification is triggered by OpenstackNodeStore.
925 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900926 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
927
Jian Li40888bf2018-11-21 09:46:32 +0900928 private boolean isRelevantHelper() {
929 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900930 }
931
932 @Override
933 public void event(OpenstackNodeEvent event) {
934 switch (event.type()) {
935 case OPENSTACK_NODE_CREATED:
936 case OPENSTACK_NODE_UPDATED:
Jian Li40888bf2018-11-21 09:46:32 +0900937 eventExecutor.execute(() -> {
938
939 if (!isRelevantHelper()) {
940 return;
941 }
942
943 bootstrapNode(event.subject());
944 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900945 break;
946 case OPENSTACK_NODE_REMOVED:
Jian Li40888bf2018-11-21 09:46:32 +0900947 eventExecutor.execute(() -> {
948
949 if (!isRelevantHelper()) {
950 return;
951 }
952
953 processOpenstackNodeRemoved(event.subject());
954 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900955 break;
Jian Li40888bf2018-11-21 09:46:32 +0900956 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900957 default:
958 break;
959 }
960 }
961 }
962}