blob: 1b934990ceaa7ca156a8054d27f8677cc61634c7 [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;
SONA Project6bc5c4a2018-12-14 23:49:52 +090082import static org.onosproject.openstacknode.api.Constants.GRE;
Jian Li2d68c192018-12-13 15:52:59 +090083import static org.onosproject.openstacknode.api.Constants.GRE_TUNNEL;
Jian Li5afbea42018-02-28 10:37:03 +090084import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090085import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
SONA Project6bc5c4a2018-12-14 23:49:52 +090086import static org.onosproject.openstacknode.api.Constants.VXLAN;
87import static org.onosproject.openstacknode.api.Constants.VXLAN_TUNNEL;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090088import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090089import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
90import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
91import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090092import static org.onosproject.openstacknode.api.NodeState.INIT;
93import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090094import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
95import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Ray Milkey8e406512018-10-24 15:56:50 -070096import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
97import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
98import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
99import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +0900100import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
101import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +0900102import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +0900103import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +0900104import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900105import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900106import static org.slf4j.LoggerFactory.getLogger;
107
108/**
109 * Service bootstraps openstack node based on its type.
110 */
Ray Milkey8e406512018-10-24 15:56:50 -0700111@Component(immediate = true,
112 property = {
113 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
114 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
115 }
116)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900117public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
118
Jian Li5afbea42018-02-28 10:37:03 +0900119 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900120
Hyunsun Moon0d457362017-06-27 17:19:41 +0900121 private static final String DEFAULT_OF_PROTO = "tcp";
122 private static final int DEFAULT_OFPORT = 6653;
123 private static final int DPID_BEGIN = 3;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126 protected CoreService coreService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 protected LeadershipService leadershipService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 protected ClusterService clusterService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900135 protected DeviceService deviceService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900138 protected DeviceAdminService deviceAdminService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900141 protected OvsdbController ovsdbController;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900144 protected OpenstackNodeService osNodeService;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900147 protected OpenstackNodeAdminService osNodeAdminService;
148
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900150 protected ComponentConfigService componentConfigService;
151
Ray Milkey8e406512018-10-24 15:56:50 -0700152 /** OVSDB server listen port. */
153 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900154
Jian Li900b7232018-12-14 14:04:51 +0900155 /** Indicates whether auto-recover openstack node status on switch re-conn event. */
Ray Milkey8e406512018-10-24 15:56:50 -0700156 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900157
Hyunsun Moon0d457362017-06-27 17:19:41 +0900158 private final ExecutorService eventExecutor = newSingleThreadExecutor(
159 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
160
161 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
162 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900163 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
164
165 private ApplicationId appId;
166 private NodeId localNode;
167
168 @Activate
169 protected void activate() {
170 appId = coreService.getAppId(APP_ID);
171 localNode = clusterService.getLocalNode().id();
172
173 componentConfigService.registerProperties(getClass());
174 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900175 deviceService.addListener(ovsdbListener);
176 deviceService.addListener(bridgeListener);
177 osNodeService.addListener(osNodeListener);
178
179 log.info("Started");
180 }
181
182 @Deactivate
183 protected void deactivate() {
184 osNodeService.removeListener(osNodeListener);
185 deviceService.removeListener(bridgeListener);
186 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900187 componentConfigService.unregisterProperties(getClass(), false);
188 leadershipService.withdraw(appId.name());
189 eventExecutor.shutdown();
190
191 log.info("Stopped");
192 }
193
194 @Modified
195 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900196 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900197
198 log.info("Modified");
199 }
200
201 @Override
202 public void processInitState(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700203 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
204 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900205 return;
206 }
207 if (!deviceService.isAvailable(osNode.intgBridge())) {
208 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
209 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900210 if (hasDpdkTunnelBridge(osNode)) {
211 createDpdkTunnelBridge(osNode);
212 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900213 }
214
215 @Override
216 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900217 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700218 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
219 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
daniel parkb18424c2018-02-05 15:43:43 +0900220 return;
221 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900222
daniel parkb18424c2018-02-05 15:43:43 +0900223 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900224 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900225 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900226 }
227
228 if (osNode.dataIp() != null &&
Jian Li2d68c192018-12-13 15:52:59 +0900229 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
230 createVxlanTunnelInterface(osNode);
231 }
232
233 if (osNode.dataIp() != null &&
234 !isIntfEnabled(osNode, GRE_TUNNEL)) {
235 createGreTunnelInterface(osNode);
daniel parkb18424c2018-02-05 15:43:43 +0900236 }
237
Daniel Parke2658ba2018-08-24 22:33:29 +0900238 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900239 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900240 .filter(dpdkintf -> dpdkintf.deviceName().equals(TUNNEL_BRIDGE))
241 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
242 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Park5a6a7102018-09-06 23:58:33 +0900243
244 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900245 .filter(dpdkintf -> dpdkintf.deviceName().equals(INTEGRATION_BRIDGE))
246 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
247 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900248 }
249
Jian Lie6312162018-03-21 21:41:00 +0900250 osNode.phyIntfs().forEach(i -> {
251 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900252 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900253 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900254 }
255 });
256
Daniel Park5a6a7102018-09-06 23:58:33 +0900257 if (osNode.vlanIntf() != null &&
258 !isIntfEnabled(osNode, osNode.vlanIntf())) {
259 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900260 osNode.vlanIntf(), deviceService, true);
Daniel Park5a6a7102018-09-06 23:58:33 +0900261 }
daniel parkb18424c2018-02-05 15:43:43 +0900262 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900263 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900264 }
265 }
266
267 @Override
268 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900269 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900270 }
271
272 @Override
273 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900274 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900275 }
276
Daniel Parke2658ba2018-08-24 22:33:29 +0900277 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
278 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
279 return osNode.dpdkConfig().dpdkIntfs().stream()
280 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
281 }
282 return false;
283 }
284
285 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
286
Ray Milkey8e406512018-10-24 15:56:50 -0700287 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900288 if (client == null) {
289 log.info("Failed to get ovsdb client");
290 return false;
291 }
292
293 return client.getBridges().stream()
294 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
295 }
296
Jian Li340165f2018-02-27 10:38:17 +0900297 /**
Jian Li340165f2018-02-27 10:38:17 +0900298 * Creates a bridge with a given name on a given openstack node.
299 *
300 * @param osNode openstack node
301 * @param bridgeName bridge name
302 * @param deviceId device identifier
303 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900304 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
305 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306
Jian Li789fadb2018-07-10 13:59:47 +0900307 List<ControllerInfo> controllers;
308
309 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
310 controllers = (List<ControllerInfo>) osNode.controllers();
311 } else {
312 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900313 .map(ControllerNode::ip)
314 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900315
Jian Li789fadb2018-07-10 13:59:47 +0900316 controllers = controllerIps.stream()
317 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
318 .collect(Collectors.toList());
319 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900320
321 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900322
Daniel Park92abf312018-08-08 17:01:35 +0900323 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900324 .name(bridgeName)
325 .failMode(BridgeDescription.FailMode.SECURE)
326 .datapathId(dpid)
327 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900328 .controllers(controllers);
329
330 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900331 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900332 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900333
334 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900335 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900336 }
337
Daniel Parke2658ba2018-08-24 22:33:29 +0900338 private void createDpdkTunnelBridge(OpenstackNode osNode) {
339 Device device = deviceService.getDevice(osNode.ovsdb());
340
341 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
342 .name(TUNNEL_BRIDGE)
343 .datapathType(NETDEV.name().toLowerCase());
344
345 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
346 bridgeConfig.addBridge(builder.build());
347 }
348
Jian Li340165f2018-02-27 10:38:17 +0900349 /**
Jian Li2d68c192018-12-13 15:52:59 +0900350 * Creates a VXLAN tunnel interface in a given openstack node.
351 *
352 * @param osNode openstack node
353 */
354 private void createVxlanTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900355 createTunnelInterface(osNode, VXLAN, VXLAN_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900356 }
357
358 /**
359 * Creates a GRE tunnel interface in a given openstack node.
360 *
361 * @param osNode openstack node
362 */
363 private void createGreTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900364 createTunnelInterface(osNode, GRE, GRE_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900365 }
366
367 /**
Jian Li340165f2018-02-27 10:38:17 +0900368 * Creates a tunnel interface in a given openstack node.
369 *
370 * @param osNode openstack node
371 */
Jian Li2d68c192018-12-13 15:52:59 +0900372 private void createTunnelInterface(OpenstackNode osNode,
SONA Project6bc5c4a2018-12-14 23:49:52 +0900373 String type, String intfName) {
Jian Li2d68c192018-12-13 15:52:59 +0900374 if (isIntfEnabled(osNode, intfName)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900375 return;
376 }
377
378 Device device = deviceService.getDevice(osNode.ovsdb());
379 if (device == null || !device.is(InterfaceConfig.class)) {
380 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
381 return;
382 }
383
Jian Li2d68c192018-12-13 15:52:59 +0900384 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900385
386 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Jian Li2d68c192018-12-13 15:52:59 +0900387 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
388 }
389
390 /**
391 * Builds tunnel description according to the network type.
392 *
393 * @param type network type
394 * @return tunnel description
395 */
SONA Project6bc5c4a2018-12-14 23:49:52 +0900396 private TunnelDescription buildTunnelDesc(String type, String intfName) {
397 if (VXLAN.equals(type) || GRE.equals(type)) {
Jian Li2d68c192018-12-13 15:52:59 +0900398 TunnelDescription.Builder tdBuilder =
399 DefaultTunnelDescription.builder()
400 .deviceId(INTEGRATION_BRIDGE)
401 .ifaceName(intfName)
402 .remote(TunnelEndPoints.flowTunnelEndpoint())
403 .key(TunnelKeys.flowTunnelKey());
404
405 switch (type) {
406 case VXLAN:
407 tdBuilder.type(TunnelDescription.Type.VXLAN);
408 break;
409 case GRE:
410 tdBuilder.type(TunnelDescription.Type.GRE);
411 break;
412 default:
413 return null;
414 }
415
416 return tdBuilder.build();
417 }
418 return null;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900419 }
420
Jian Li340165f2018-02-27 10:38:17 +0900421 /**
Jian Li5ecfd1a2018-12-10 11:41:03 +0900422 * Checks whether a given network interface in a given openstack node
423 * is enabled or not.
Jian Li340165f2018-02-27 10:38:17 +0900424 *
425 * @param osNode openstack node
426 * @param intf network interface name
427 * @return true if the given interface is enabled, false otherwise
428 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900429 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900430 return deviceService.isAvailable(osNode.intgBridge()) &&
431 deviceService.getPorts(osNode.intgBridge()).stream()
432 .anyMatch(port -> Objects.equals(
433 port.annotations().value(PORT_NAME), intf) &&
434 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900435 }
436
Jian Li340165f2018-02-27 10:38:17 +0900437 /**
438 * Checks whether all requirements for this state are fulfilled or not.
439 *
440 * @param osNode openstack node
441 * @return true if all requirements are fulfilled, false otherwise
442 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900443 private boolean isCurrentStateDone(OpenstackNode osNode) {
444 switch (osNode.state()) {
445 case INIT:
Jian Li5ecfd1a2018-12-10 11:41:03 +0900446 if (!isOvsdbConnected(osNode, ovsdbPortNum,
447 ovsdbController, deviceService)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900448 return false;
449 }
450
Daniel Parke2658ba2018-08-24 22:33:29 +0900451 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
452 if (hasDpdkTunnelBridge(osNode)) {
453 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
454 }
455 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900456 case DEVICE_CREATED:
457 if (osNode.dataIp() != null &&
Jian Li2d68c192018-12-13 15:52:59 +0900458 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
459 return false;
460 }
461 if (osNode.dataIp() != null &&
462 !isIntfEnabled(osNode, GRE_TUNNEL)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900463 return false;
464 }
465 if (osNode.vlanIntf() != null &&
466 !isIntfEnabled(osNode, osNode.vlanIntf())) {
467 return false;
468 }
daniel parkb18424c2018-02-05 15:43:43 +0900469 if (osNode.type() == GATEWAY &&
470 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900471 return false;
472 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900473 if (osNode.dpdkConfig() != null &&
474 osNode.dpdkConfig().dpdkIntfs() != null &&
475 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
476 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900477 }
Jian Lie6312162018-03-21 21:41:00 +0900478
479 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
480 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
481 return false;
482 }
483 }
484
Hyunsun Moon0d457362017-06-27 17:19:41 +0900485 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900486 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900487 case INCOMPLETE:
488 // always return false
489 // run init CLI to re-trigger node bootstrap
490 return false;
491 default:
492 return true;
493 }
494 }
495
Jian Li40888bf2018-11-21 09:46:32 +0900496 private boolean isDpdkIntfsCreated(OpenstackNode osNode,
497 Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700498 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900499 if (client == null) {
500 log.info("Failed to get ovsdb client");
501 return false;
502 }
503
504 Set<OvsdbPort> ports = client.getPorts();
505
Jian Li5ecfd1a2018-12-10 11:41:03 +0900506 for (DpdkInterface dpdkIntf : dpdkInterfaces) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900507 Optional<OvsdbPort> port = ports.stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900508 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkIntf.intf()))
Daniel Parke2658ba2018-08-24 22:33:29 +0900509 .findAny();
510
511 if (!port.isPresent()) {
512 return false;
513 }
Jian Li5ecfd1a2018-12-10 11:41:03 +0900514 Interface intf = client.getInterface(dpdkIntf.intf());
Daniel Parke2658ba2018-08-24 22:33:29 +0900515 if (intf == null) {
516 return false;
517 }
518
519 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
520 if (mtu == null) {
521 return false;
522 }
523
524 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
525 if (option == null) {
526 return false;
527 }
528
Jian Li5ecfd1a2018-12-10 11:41:03 +0900529 if (!mtu.set().contains(dpdkIntf.mtu().intValue()) ||
530 !option.toString().contains(dpdkIntf.pciAddress())) {
Jian Li40888bf2018-11-21 09:46:32 +0900531 log.trace("The dpdk interface {} was created but mtu or " +
532 "pci address is different from the config.");
Daniel Parke2658ba2018-08-24 22:33:29 +0900533 return false;
534 }
535 }
536 return true;
537 }
538
Jian Li340165f2018-02-27 10:38:17 +0900539 /**
540 * Configures the openstack node with new state.
541 *
542 * @param osNode openstack node
543 * @param newState a new state
544 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900545 private void setState(OpenstackNode osNode, NodeState newState) {
546 if (osNode.state() == newState) {
547 return;
548 }
549 OpenstackNode updated = osNode.updateState(newState);
550 osNodeAdminService.updateNode(updated);
551 log.info("Changed {} state: {}", osNode.hostname(), newState);
552 }
553
Jian Li340165f2018-02-27 10:38:17 +0900554 /**
555 * Bootstraps a new openstack node.
556 *
557 * @param osNode openstack node
558 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900559 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900560 if (osNode.type() == CONTROLLER) {
561 if (osNode.state() == INIT && checkEndpoint(osNode)) {
562 setState(osNode, COMPLETE);
563 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900564 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900565 if (isCurrentStateDone(osNode)) {
566 setState(osNode, osNode.state().nextState());
567 } else {
Jian Li97482c12018-07-03 01:08:23 +0900568 log.trace("Processing {} state for {}", osNode.state(),
569 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900570 osNode.state().process(this, osNode);
571 }
572 }
573 }
574
Daniel Park5a6a7102018-09-06 23:58:33 +0900575 private void removeVlanInterface(OpenstackNode osNode) {
576 if (osNode.vlanIntf() != null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900577 Optional<DpdkInterface> dpdkIntf =
578 dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900579
Jian Li5ecfd1a2018-12-10 11:41:03 +0900580 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900581 }
582 }
583
584 private void removePhysicalInterface(OpenstackNode osNode) {
585 osNode.phyIntfs().forEach(phyIntf -> {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900586 Optional<DpdkInterface> dpdkIntf = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900587
Jian Li5ecfd1a2018-12-10 11:41:03 +0900588 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900589 });
590 }
591
Jian Li5ecfd1a2018-12-10 11:41:03 +0900592 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode,
593 String intf) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900594 return osNode.dpdkConfig() == null ? Optional.empty() :
595 osNode.dpdkConfig().dpdkIntfs().stream()
596 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
597 .findAny();
598 }
599
600 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
601 String intfName,
602 Optional<DpdkInterface> dpdkInterface) {
603 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700604 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900605 ovsdbController, false);
606 } else {
607 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
608 false);
609 }
610 }
611
612 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700613 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Park489645c2018-10-24 11:34:22 +0900614 if (client == null) {
615 log.info("Failed to get ovsdb client");
616 return;
617 }
618
Daniel Park5a6a7102018-09-06 23:58:33 +0900619 //delete physical interfaces from the node
620 removePhysicalInterface(osNode);
621
622 //delete vlan interface from the node
623 removeVlanInterface(osNode);
624
625 //delete dpdk interfaces from the node
626 if (osNode.dpdkConfig() != null) {
627 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
628 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
Jian Li40888bf2018-11-21 09:46:32 +0900629 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum,
630 ovsdbController, false);
Daniel Park5a6a7102018-09-06 23:58:33 +0900631 }
632 });
633 }
Daniel Park489645c2018-10-24 11:34:22 +0900634
635 //delete tunnel bridge from the node
636 if (hasDpdkTunnelBridge(osNode)) {
637 client.dropBridge(TUNNEL_BRIDGE);
638 }
639
640 //delete integration bridge from the node
641 client.dropBridge(INTEGRATION_BRIDGE);
642
643 //disconnect ovsdb
644 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900645 }
646
Jian Li51b844c2018-05-31 10:59:03 +0900647 /**
648 * Checks the validity of the given endpoint.
649 *
650 * @param osNode gateway node
651 * @return validity result
652 */
653 private boolean checkEndpoint(OpenstackNode osNode) {
654 if (osNode == null) {
655 log.warn("Keystone auth info has not been configured. " +
656 "Please specify auth info via network-cfg.json.");
657 return false;
658 }
659
660 OSClient client = getConnectedClient(osNode);
661
662 if (client == null) {
663 return false;
664 } else {
665 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900666 }
667 }
668
Jian Li340165f2018-02-27 10:38:17 +0900669 /**
Jian Li97482c12018-07-03 01:08:23 +0900670 * Extracts properties from the component configuration context.
671 *
672 * @param context the component context
673 */
674 private void readComponentConfiguration(ComponentContext context) {
675 Dictionary<?, ?> properties = context.getProperties();
676
677 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
678 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700679 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
680 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900681 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700682 ovsdbPortNum = ovsdbPortConfigured;
683 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900684 }
685
686 Boolean autoRecoveryConfigured =
687 getBooleanProperty(properties, AUTO_RECOVERY);
688 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700689 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900690 log.info("Auto recovery flag is NOT " +
691 "configured, default value is {}", autoRecovery);
692 } else {
693 autoRecovery = autoRecoveryConfigured;
694 log.info("Configured. Auto recovery flag is {}", autoRecovery);
695 }
696 }
697
698 /**
Jian Li340165f2018-02-27 10:38:17 +0900699 * An internal OVSDB listener. This listener is used for listening the
700 * network facing events from OVSDB device. If a new OVSDB device is detected,
701 * ONOS tries to bootstrap the openstack node.
702 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900703 private class InternalOvsdbListener implements DeviceListener {
704
705 @Override
706 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900707 return event.subject().type() == Device.Type.CONTROLLER;
708 }
709
710 private boolean isRelevantHelper() {
711 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900712 }
713
714 @Override
715 public void event(DeviceEvent event) {
716 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900717
718 switch (event.type()) {
719 case DEVICE_AVAILABILITY_CHANGED:
720 case DEVICE_ADDED:
721 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900722
723 if (!isRelevantHelper()) {
724 return;
725 }
726
Jian Licab41762018-11-06 12:30:08 +0900727 OpenstackNode osNode = osNodeService.node(device.id());
728
729 if (osNode == null || osNode.type() == CONTROLLER) {
730 return;
731 }
732
Hyunsun Moon0d457362017-06-27 17:19:41 +0900733 if (deviceService.isAvailable(device.id())) {
734 log.debug("OVSDB {} detected", device.id());
735 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900736 }
737 });
738 break;
739 case PORT_ADDED:
740 case PORT_REMOVED:
741 case DEVICE_REMOVED:
742 default:
743 // do nothing
744 break;
745 }
746 }
747 }
748
Jian Li340165f2018-02-27 10:38:17 +0900749 /**
750 * An internal integration bridge listener. This listener is used for
751 * listening the events from integration bridge. To listen the events from
752 * other types of bridge such as provider bridge or tunnel bridge, we need
753 * to augment OpenstackNodeService.node() method.
754 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900755 private class InternalBridgeListener implements DeviceListener {
756
757 @Override
758 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900759 return event.subject().type() == Device.Type.SWITCH;
760 }
761
762 private boolean isRelevantHelper() {
763 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900764 }
765
766 @Override
767 public void event(DeviceEvent event) {
768 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900769
770 switch (event.type()) {
771 case DEVICE_AVAILABILITY_CHANGED:
772 case DEVICE_ADDED:
773 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900774
775 if (!isRelevantHelper()) {
776 return;
777 }
778
Jian Licab41762018-11-06 12:30:08 +0900779 OpenstackNode osNode = osNodeService.node(device.id());
780
781 if (osNode == null || osNode.type() == CONTROLLER) {
782 return;
783 }
784
Hyunsun Moon0d457362017-06-27 17:19:41 +0900785 if (deviceService.isAvailable(device.id())) {
786 log.debug("Integration bridge created on {}", osNode.hostname());
787 bootstrapNode(osNode);
788 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900789 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900790 setState(osNode, INCOMPLETE);
791 }
Jian Li97482c12018-07-03 01:08:23 +0900792
793 if (autoRecovery) {
794 if (osNode.state() == INCOMPLETE ||
795 osNode.state() == DEVICE_CREATED) {
796 log.info("Device {} is reconnected", device.id());
797 osNodeAdminService.updateNode(
798 osNode.updateState(NodeState.INIT));
799 }
800 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900801 });
802 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900803 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900804 case PORT_ADDED:
805 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900806
807 if (!isRelevantHelper()) {
808 return;
809 }
810
Jian Licab41762018-11-06 12:30:08 +0900811 OpenstackNode osNode = osNodeService.node(device.id());
812
813 if (osNode == null || osNode.type() == CONTROLLER) {
814 return;
815 }
816
Hyunsun Moon0d457362017-06-27 17:19:41 +0900817 Port port = event.port();
818 String portName = port.annotations().value(PORT_NAME);
819 if (osNode.state() == DEVICE_CREATED && (
Jian Li2d68c192018-12-13 15:52:59 +0900820 Objects.equals(portName, VXLAN_TUNNEL) ||
821 Objects.equals(portName, GRE_TUNNEL) ||
Hyunsun Moon0d457362017-06-27 17:19:41 +0900822 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900823 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900824 containsPhyIntf(osNode, portName)) ||
825 containsDpdkIntfs(osNode, portName)) {
826 log.info("Interface {} added or updated to {}",
827 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900828 bootstrapNode(osNode);
829 }
830 });
831 break;
832 case PORT_REMOVED:
833 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900834
835 if (!isRelevantHelper()) {
836 return;
837 }
838
Jian Licab41762018-11-06 12:30:08 +0900839 OpenstackNode osNode = osNodeService.node(device.id());
840
841 if (osNode == null || osNode.type() == CONTROLLER) {
842 return;
843 }
844
Hyunsun Moon0d457362017-06-27 17:19:41 +0900845 Port port = event.port();
846 String portName = port.annotations().value(PORT_NAME);
847 if (osNode.state() == COMPLETE && (
Jian Li2d68c192018-12-13 15:52:59 +0900848 Objects.equals(portName, VXLAN_TUNNEL) ||
849 Objects.equals(portName, GRE_TUNNEL) ||
Hyunsun Moon0d457362017-06-27 17:19:41 +0900850 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900851 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900852 containsPhyIntf(osNode, portName)) ||
853 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900854 log.warn("Interface {} removed from {}",
855 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900856 setState(osNode, INCOMPLETE);
857 }
858 });
859 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900860 case DEVICE_REMOVED:
861 default:
862 // do nothing
863 break;
864 }
865 }
866 }
867
Jian Li340165f2018-02-27 10:38:17 +0900868 /**
Jian Lie6312162018-03-21 21:41:00 +0900869 * Checks whether the openstack node contains the given physical interface.
870 *
871 * @param osNode openstack node
872 * @param portName physical interface
873 * @return true if openstack node contains the given physical interface,
874 * false otherwise
875 */
876 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900877 return osNode.phyIntfs().stream()
878 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
879 }
Jian Lie6312162018-03-21 21:41:00 +0900880
Daniel Park5a6a7102018-09-06 23:58:33 +0900881 /**
882 * Checks whether the openstack node contains the given dpdk interface.
883 *
884 * @param osNode openstack node
885 * @param portName dpdk interface
886 * @return true if openstack node contains the given dpdk interface,
887 * false otherwise
888 */
889 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
890 if (osNode.dpdkConfig() == null) {
891 return false;
892 }
893 return osNode.dpdkConfig().dpdkIntfs().stream()
894 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900895 }
896
897 /**
Jian Li340165f2018-02-27 10:38:17 +0900898 * An internal openstack node listener.
899 * The notification is triggered by OpenstackNodeStore.
900 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900901 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
902
Jian Li40888bf2018-11-21 09:46:32 +0900903 private boolean isRelevantHelper() {
904 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900905 }
906
907 @Override
908 public void event(OpenstackNodeEvent event) {
909 switch (event.type()) {
910 case OPENSTACK_NODE_CREATED:
911 case OPENSTACK_NODE_UPDATED:
Jian Li40888bf2018-11-21 09:46:32 +0900912 eventExecutor.execute(() -> {
913
914 if (!isRelevantHelper()) {
915 return;
916 }
917
918 bootstrapNode(event.subject());
919 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900920 break;
921 case OPENSTACK_NODE_REMOVED:
Jian Li40888bf2018-11-21 09:46:32 +0900922 eventExecutor.execute(() -> {
923
924 if (!isRelevantHelper()) {
925 return;
926 }
927
928 processOpenstackNodeRemoved(event.subject());
929 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900930 break;
Jian Li40888bf2018-11-21 09:46:32 +0900931 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900932 default:
933 break;
934 }
935 }
936 }
937}