blob: bdd1de29ee5112c34f8cd279e9896277eba6f005 [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;
Jian Li62116942019-09-03 23:10:20 +090033import org.onosproject.net.behaviour.BridgeName;
Hyunsun Moon0d457362017-06-27 17:19:41 +090034import org.onosproject.net.behaviour.ControllerInfo;
35import org.onosproject.net.behaviour.DefaultBridgeDescription;
Jian Li62116942019-09-03 23:10:20 +090036import org.onosproject.net.behaviour.DefaultPatchDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090037import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090038import org.onosproject.net.behaviour.InterfaceConfig;
Jian Li62116942019-09-03 23:10:20 +090039import org.onosproject.net.behaviour.PatchDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090040import org.onosproject.net.behaviour.TunnelDescription;
41import org.onosproject.net.behaviour.TunnelEndPoints;
42import org.onosproject.net.behaviour.TunnelKeys;
43import org.onosproject.net.device.DeviceAdminService;
44import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
46import org.onosproject.net.device.DeviceService;
Daniel Parke2658ba2018-08-24 22:33:29 +090047import org.onosproject.openstacknode.api.DpdkInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090048import org.onosproject.openstacknode.api.NodeState;
49import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090050import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
51import org.onosproject.openstacknode.api.OpenstackNodeEvent;
52import org.onosproject.openstacknode.api.OpenstackNodeHandler;
53import org.onosproject.openstacknode.api.OpenstackNodeListener;
54import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090055import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Daniel Parke2658ba2018-08-24 22:33:29 +090056import org.onosproject.ovsdb.controller.OvsdbClientService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090057import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Parke2658ba2018-08-24 22:33:29 +090058import org.onosproject.ovsdb.controller.OvsdbPort;
59import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
60import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
61import org.onosproject.ovsdb.rfc.table.Interface;
Jian Li51b844c2018-05-31 10:59:03 +090062import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090063import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070064import org.osgi.service.component.annotations.Activate;
65import org.osgi.service.component.annotations.Component;
66import org.osgi.service.component.annotations.Deactivate;
67import org.osgi.service.component.annotations.Modified;
68import org.osgi.service.component.annotations.Reference;
69import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import org.slf4j.Logger;
71
Daniel Parke2658ba2018-08-24 22:33:29 +090072import java.util.Collection;
Hyunsun Moon0d457362017-06-27 17:19:41 +090073import java.util.Dictionary;
74import java.util.List;
75import java.util.Objects;
Daniel Parke2658ba2018-08-24 22:33:29 +090076import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090077import java.util.Set;
78import java.util.concurrent.ExecutorService;
79import java.util.stream.Collectors;
80
Hyunsun Moon0d457362017-06-27 17:19:41 +090081import static java.util.concurrent.Executors.newSingleThreadExecutor;
82import static org.onlab.packet.TpPort.tpPort;
83import static org.onlab.util.Tools.groupedThreads;
84import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li62116942019-09-03 23:10:20 +090085import static org.onosproject.openstacknode.api.Constants.BRIDGE_PREFIX;
Jian Li621f73c2018-12-15 01:49:22 +090086import static org.onosproject.openstacknode.api.Constants.GENEVE;
87import static org.onosproject.openstacknode.api.Constants.GENEVE_TUNNEL;
SONA Project6bc5c4a2018-12-14 23:49:52 +090088import static org.onosproject.openstacknode.api.Constants.GRE;
Jian Li2d68c192018-12-13 15:52:59 +090089import static org.onosproject.openstacknode.api.Constants.GRE_TUNNEL;
Jian Li5afbea42018-02-28 10:37:03 +090090import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Jian Li62116942019-09-03 23:10:20 +090091import static org.onosproject.openstacknode.api.Constants.INTEGRATION_TO_PHYSICAL_PREFIX;
92import static org.onosproject.openstacknode.api.Constants.PHYSICAL_TO_INTEGRATION_SUFFIX;
Daniel Parke2658ba2018-08-24 22:33:29 +090093import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
SONA Project6bc5c4a2018-12-14 23:49:52 +090094import static org.onosproject.openstacknode.api.Constants.VXLAN;
95import static org.onosproject.openstacknode.api.Constants.VXLAN_TUNNEL;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090096import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090097import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
98import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
99import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +0900100import static org.onosproject.openstacknode.api.NodeState.INIT;
101import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900102import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
103import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Ray Milkey8e406512018-10-24 15:56:50 -0700104import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
105import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
106import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
107import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +0900108import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
109import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +0900110import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +0900111import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +0900112import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900113import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Jian Li62116942019-09-03 23:10:20 +0900114import static org.onosproject.openstacknode.util.OpenstackNodeUtil.structurePortName;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900115import static org.slf4j.LoggerFactory.getLogger;
116
117/**
118 * Service bootstraps openstack node based on its type.
119 */
Ray Milkey8e406512018-10-24 15:56:50 -0700120@Component(immediate = true,
121 property = {
122 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
123 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
124 }
125)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
127
Jian Li5afbea42018-02-28 10:37:03 +0900128 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129
Hyunsun Moon0d457362017-06-27 17:19:41 +0900130 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Park4b24cec2018-11-28 19:21:25 +0900131 private static final String NO_OVSDB_CLIENT_MSG = "Failed to get ovsdb client";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 private static final int DEFAULT_OFPORT = 6653;
133 private static final int DPID_BEGIN = 3;
Jian Li62116942019-09-03 23:10:20 +0900134 private static final int NETWORK_BEGIN = 3;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900137 protected CoreService coreService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900140 protected LeadershipService leadershipService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900143 protected ClusterService clusterService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900146 protected DeviceService deviceService;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900149 protected DeviceAdminService deviceAdminService;
150
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900152 protected OvsdbController ovsdbController;
153
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900155 protected OpenstackNodeService osNodeService;
156
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900158 protected OpenstackNodeAdminService osNodeAdminService;
159
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900161 protected ComponentConfigService componentConfigService;
162
Ray Milkey8e406512018-10-24 15:56:50 -0700163 /** OVSDB server listen port. */
164 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900165
Jian Li900b7232018-12-14 14:04:51 +0900166 /** Indicates whether auto-recover openstack node status on switch re-conn event. */
Ray Milkey8e406512018-10-24 15:56:50 -0700167 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900168
Hyunsun Moon0d457362017-06-27 17:19:41 +0900169 private final ExecutorService eventExecutor = newSingleThreadExecutor(
170 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
171
172 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
173 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900174 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
175
176 private ApplicationId appId;
177 private NodeId localNode;
178
179 @Activate
180 protected void activate() {
181 appId = coreService.getAppId(APP_ID);
182 localNode = clusterService.getLocalNode().id();
183
184 componentConfigService.registerProperties(getClass());
185 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900186 deviceService.addListener(ovsdbListener);
187 deviceService.addListener(bridgeListener);
188 osNodeService.addListener(osNodeListener);
189
190 log.info("Started");
191 }
192
193 @Deactivate
194 protected void deactivate() {
195 osNodeService.removeListener(osNodeListener);
196 deviceService.removeListener(bridgeListener);
197 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900198 componentConfigService.unregisterProperties(getClass(), false);
199 leadershipService.withdraw(appId.name());
200 eventExecutor.shutdown();
201
202 log.info("Stopped");
203 }
204
205 @Modified
206 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900207 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900208
209 log.info("Modified");
210 }
211
212 @Override
213 public void processInitState(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700214 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
215 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900216 return;
217 }
Jian Li62116942019-09-03 23:10:20 +0900218
Hyunsun Moon0d457362017-06-27 17:19:41 +0900219 if (!deviceService.isAvailable(osNode.intgBridge())) {
220 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
221 }
Jian Li62116942019-09-03 23:10:20 +0900222
Daniel Parke2658ba2018-08-24 22:33:29 +0900223 if (hasDpdkTunnelBridge(osNode)) {
224 createDpdkTunnelBridge(osNode);
225 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900226 }
227
228 @Override
229 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900230 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700231 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
232 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
daniel parkb18424c2018-02-05 15:43:43 +0900233 return;
234 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900235
daniel parkb18424c2018-02-05 15:43:43 +0900236 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900237 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900238 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900239 }
240
241 if (osNode.dataIp() != null &&
Jian Li2d68c192018-12-13 15:52:59 +0900242 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
243 createVxlanTunnelInterface(osNode);
244 }
245
246 if (osNode.dataIp() != null &&
247 !isIntfEnabled(osNode, GRE_TUNNEL)) {
248 createGreTunnelInterface(osNode);
daniel parkb18424c2018-02-05 15:43:43 +0900249 }
250
Jian Li621f73c2018-12-15 01:49:22 +0900251 if (osNode.dataIp() != null &&
252 !isIntfEnabled(osNode, GENEVE_TUNNEL)) {
253 createGeneveTunnelInterface(osNode);
254 }
255
Daniel Parke2658ba2018-08-24 22:33:29 +0900256 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900257 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900258 .filter(dpdkintf -> dpdkintf.deviceName().equals(TUNNEL_BRIDGE))
259 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
260 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Park5a6a7102018-09-06 23:58:33 +0900261
262 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900263 .filter(dpdkintf -> dpdkintf.deviceName().equals(INTEGRATION_BRIDGE))
264 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
265 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900266 }
267
Jian Li62116942019-09-03 23:10:20 +0900268 // provision new physical interfaces on the given node
269 // this includes creating physical bridge, attaching physical port
270 // to physical bridge, adding patch ports to both physical bridge and br-int
271
272 provisionPhysicalInterfaces(osNode);
Jian Lie6312162018-03-21 21:41:00 +0900273
Daniel Park5a6a7102018-09-06 23:58:33 +0900274 if (osNode.vlanIntf() != null &&
275 !isIntfEnabled(osNode, osNode.vlanIntf())) {
276 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900277 osNode.vlanIntf(), deviceService, true);
Daniel Park5a6a7102018-09-06 23:58:33 +0900278 }
daniel parkb18424c2018-02-05 15:43:43 +0900279 } catch (Exception e) {
Jian Li621f73c2018-12-15 01:49:22 +0900280 log.error("Exception occurred because of {}", e);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900281 }
282 }
283
284 @Override
285 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900286 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900287 }
288
289 @Override
290 public void processIncompleteState(OpenstackNode osNode) {
Daniel Park4b24cec2018-11-28 19:21:25 +0900291 //Do nothing for now
Hyunsun Moon0d457362017-06-27 17:19:41 +0900292 }
293
Daniel Parke2658ba2018-08-24 22:33:29 +0900294 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
295 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
296 return osNode.dpdkConfig().dpdkIntfs().stream()
297 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
298 }
299 return false;
300 }
301
302 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
303
Ray Milkey8e406512018-10-24 15:56:50 -0700304 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900305 if (client == null) {
Daniel Park4b24cec2018-11-28 19:21:25 +0900306 log.info(NO_OVSDB_CLIENT_MSG);
Daniel Parke2658ba2018-08-24 22:33:29 +0900307 return false;
308 }
309
310 return client.getBridges().stream()
311 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
312 }
313
Jian Li340165f2018-02-27 10:38:17 +0900314 /**
Jian Li340165f2018-02-27 10:38:17 +0900315 * Creates a bridge with a given name on a given openstack node.
316 *
317 * @param osNode openstack node
318 * @param bridgeName bridge name
319 * @param deviceId device identifier
320 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900321 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
322 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900323
Jian Li789fadb2018-07-10 13:59:47 +0900324 List<ControllerInfo> controllers;
325
326 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
327 controllers = (List<ControllerInfo>) osNode.controllers();
328 } else {
329 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900330 .map(ControllerNode::ip)
331 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900332
Jian Li789fadb2018-07-10 13:59:47 +0900333 controllers = controllerIps.stream()
334 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
335 .collect(Collectors.toList());
336 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900337
338 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900339
Daniel Park92abf312018-08-08 17:01:35 +0900340 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900341 .name(bridgeName)
342 .failMode(BridgeDescription.FailMode.SECURE)
343 .datapathId(dpid)
344 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900345 .controllers(controllers);
346
347 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900348 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900349 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900350
351 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900352 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900353 }
354
Daniel Parke2658ba2018-08-24 22:33:29 +0900355 private void createDpdkTunnelBridge(OpenstackNode osNode) {
356 Device device = deviceService.getDevice(osNode.ovsdb());
357
358 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
359 .name(TUNNEL_BRIDGE)
360 .datapathType(NETDEV.name().toLowerCase());
361
362 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
363 bridgeConfig.addBridge(builder.build());
364 }
365
Jian Li62116942019-09-03 23:10:20 +0900366 private void provisionPhysicalInterfaces(OpenstackNode osNode) {
367 osNode.phyIntfs().forEach(pi -> {
368 String bridgeName = BRIDGE_PREFIX + pi.network();
369 String patchPortName =
370 structurePortName(INTEGRATION_TO_PHYSICAL_PREFIX + pi.network());
371
372 if (!hasPhyBridge(osNode, bridgeName)) {
373 createPhysicalBridge(osNode, pi);
374 createPhysicalPatchPorts(osNode, pi);
375 attachPhysicalPort(osNode, pi);
376 } else {
377 // in case physical bridge exists, but patch port is missing on br-int,
378 // we will add patch port to connect br-int with physical bridge
379 if (!hasPhyPatchPort(osNode, patchPortName)) {
380 createPhysicalPatchPorts(osNode, pi);
381 }
382 }
383 });
384 }
385
386 private void cleanPhysicalInterfaces(OpenstackNode osNode) {
387 Device device = deviceService.getDevice(osNode.ovsdb());
388
389 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
390
391 Set<String> bridgeNames = bridgeConfig.getBridges().stream()
392 .map(BridgeDescription::name).collect(Collectors.toSet());
393
394 Set<String> phyNetworkNames = osNode.phyIntfs().stream()
395 .map(pi -> BRIDGE_PREFIX + pi.network()).collect(Collectors.toSet());
396
397 // we remove existing physical bridges and patch ports, if the physical
398 // bridges are not defined in openstack node
399 bridgeNames.forEach(brName -> {
400 if (!phyNetworkNames.contains(brName) && !brName.equals(INTEGRATION_BRIDGE)) {
401 removePhysicalPatchPorts(osNode, brName.substring(NETWORK_BEGIN));
402 removePhysicalBridge(osNode, brName.substring(NETWORK_BEGIN));
403 }
404 });
405 }
406
407 private void unprovisionPhysicalInterfaces(OpenstackNode osNode) {
408 osNode.phyIntfs().forEach(pi -> {
409 detachPhysicalPort(osNode, pi.network(), pi.intf());
410 removePhysicalPatchPorts(osNode, pi.network());
411 removePhysicalBridge(osNode, pi.network());
412 });
413 }
414
415 private void createPhysicalBridge(OpenstackNode osNode,
416 OpenstackPhyInterface phyInterface) {
417 Device device = deviceService.getDevice(osNode.ovsdb());
418
419 String bridgeName = BRIDGE_PREFIX + phyInterface.network();
420
421 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
422 .name(bridgeName);
423
424 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
425 bridgeConfig.addBridge(builder.build());
426 }
427
428 private void removePhysicalBridge(OpenstackNode osNode, String network) {
429 Device device = deviceService.getDevice(osNode.ovsdb());
430
431 BridgeName bridgeName = BridgeName.bridgeName(BRIDGE_PREFIX + network);
432
433 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
434 bridgeConfig.deleteBridge(bridgeName);
435 }
436
437 private void createPhysicalPatchPorts(OpenstackNode osNode,
438 OpenstackPhyInterface phyInterface) {
439 Device device = deviceService.getDevice(osNode.ovsdb());
440
441 if (device == null || !device.is(InterfaceConfig.class)) {
442 log.error("Failed to create patch interface on {}", osNode.ovsdb());
443 return;
444 }
445
446 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
447
448 String intToPhyPatchPort = structurePortName(
449 INTEGRATION_TO_PHYSICAL_PREFIX + phyInterface.network());
450 String phyToIntPatchPort = structurePortName(
451 phyInterface.network() + PHYSICAL_TO_INTEGRATION_SUFFIX);
452
453 // integration bridge -> physical bridge
454 PatchDescription intToPhyPatchDesc =
455 DefaultPatchDescription.builder()
456 .deviceId(INTEGRATION_BRIDGE)
457 .ifaceName(intToPhyPatchPort)
458 .peer(phyToIntPatchPort)
459 .build();
460
461 // physical bridge -> integration bridge
462 PatchDescription phyToIntPatchDesc =
463 DefaultPatchDescription.builder()
464 .deviceId(physicalDeviceId)
465 .ifaceName(phyToIntPatchPort)
466 .peer(intToPhyPatchPort)
467 .build();
468
469 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
470 ifaceConfig.addPatchMode(INTEGRATION_TO_PHYSICAL_PREFIX +
471 phyInterface.network(), intToPhyPatchDesc);
472 ifaceConfig.addPatchMode(phyInterface.network() +
473 PHYSICAL_TO_INTEGRATION_SUFFIX, phyToIntPatchDesc);
474
475 addOrRemoveSystemInterface(osNode, physicalDeviceId,
476 phyInterface.intf(), deviceService, true);
477 }
478
479 private void removePhysicalPatchPorts(OpenstackNode osNode, String network) {
480 Device device = deviceService.getDevice(osNode.ovsdb());
481
482 if (device == null || !device.is(InterfaceConfig.class)) {
483 log.error("Failed to remove patch interface on {}", osNode.ovsdb());
484 return;
485 }
486
487 String intToPhyPatchPort = structurePortName(
488 INTEGRATION_TO_PHYSICAL_PREFIX + network);
489
490 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
491 ifaceConfig.removePatchMode(intToPhyPatchPort);
492 }
493
494 private void attachPhysicalPort(OpenstackNode osNode,
495 OpenstackPhyInterface phyInterface) {
496
497 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
498
499 addOrRemoveSystemInterface(osNode, physicalDeviceId,
500 phyInterface.intf(), deviceService, true);
501 }
502
503 private void detachPhysicalPort(OpenstackNode osNode, String network, String portName) {
504 String physicalDeviceId = BRIDGE_PREFIX + network;
505
506 addOrRemoveSystemInterface(osNode, physicalDeviceId, portName, deviceService, false);
507 }
508
Jian Li340165f2018-02-27 10:38:17 +0900509 /**
Jian Li2d68c192018-12-13 15:52:59 +0900510 * Creates a VXLAN tunnel interface in a given openstack node.
511 *
512 * @param osNode openstack node
513 */
514 private void createVxlanTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900515 createTunnelInterface(osNode, VXLAN, VXLAN_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900516 }
517
518 /**
519 * Creates a GRE tunnel interface in a given openstack node.
520 *
521 * @param osNode openstack node
522 */
523 private void createGreTunnelInterface(OpenstackNode osNode) {
SONA Project6bc5c4a2018-12-14 23:49:52 +0900524 createTunnelInterface(osNode, GRE, GRE_TUNNEL);
Jian Li2d68c192018-12-13 15:52:59 +0900525 }
526
527 /**
Jian Li621f73c2018-12-15 01:49:22 +0900528 * Creates a GENEVE tunnel interface in a given openstack node.
529 *
530 * @param osNode openstack node
531 */
532 private void createGeneveTunnelInterface(OpenstackNode osNode) {
533 createTunnelInterface(osNode, GENEVE, GENEVE_TUNNEL);
534 }
535
536 /**
Jian Li340165f2018-02-27 10:38:17 +0900537 * Creates a tunnel interface in a given openstack node.
538 *
539 * @param osNode openstack node
540 */
Jian Li2d68c192018-12-13 15:52:59 +0900541 private void createTunnelInterface(OpenstackNode osNode,
SONA Project6bc5c4a2018-12-14 23:49:52 +0900542 String type, String intfName) {
Jian Li2d68c192018-12-13 15:52:59 +0900543 if (isIntfEnabled(osNode, intfName)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900544 return;
545 }
546
547 Device device = deviceService.getDevice(osNode.ovsdb());
548 if (device == null || !device.is(InterfaceConfig.class)) {
549 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
550 return;
551 }
552
Jian Li2d68c192018-12-13 15:52:59 +0900553 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900554
555 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Jian Li2d68c192018-12-13 15:52:59 +0900556 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
557 }
558
559 /**
560 * Builds tunnel description according to the network type.
561 *
562 * @param type network type
563 * @return tunnel description
564 */
SONA Project6bc5c4a2018-12-14 23:49:52 +0900565 private TunnelDescription buildTunnelDesc(String type, String intfName) {
Jian Li621f73c2018-12-15 01:49:22 +0900566 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
Jian Li2d68c192018-12-13 15:52:59 +0900567 TunnelDescription.Builder tdBuilder =
568 DefaultTunnelDescription.builder()
569 .deviceId(INTEGRATION_BRIDGE)
570 .ifaceName(intfName)
571 .remote(TunnelEndPoints.flowTunnelEndpoint())
572 .key(TunnelKeys.flowTunnelKey());
573
574 switch (type) {
575 case VXLAN:
576 tdBuilder.type(TunnelDescription.Type.VXLAN);
577 break;
578 case GRE:
579 tdBuilder.type(TunnelDescription.Type.GRE);
580 break;
Jian Li621f73c2018-12-15 01:49:22 +0900581 case GENEVE:
582 tdBuilder.type(TunnelDescription.Type.GENEVE);
583 break;
Jian Li2d68c192018-12-13 15:52:59 +0900584 default:
585 return null;
586 }
587
588 return tdBuilder.build();
589 }
590 return null;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900591 }
592
Jian Li340165f2018-02-27 10:38:17 +0900593 /**
Jian Li5ecfd1a2018-12-10 11:41:03 +0900594 * Checks whether a given network interface in a given openstack node
595 * is enabled or not.
Jian Li340165f2018-02-27 10:38:17 +0900596 *
597 * @param osNode openstack node
598 * @param intf network interface name
599 * @return true if the given interface is enabled, false otherwise
600 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900601 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900602 return deviceService.isAvailable(osNode.intgBridge()) &&
603 deviceService.getPorts(osNode.intgBridge()).stream()
604 .anyMatch(port -> Objects.equals(
605 port.annotations().value(PORT_NAME), intf) &&
606 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900607 }
608
Jian Li62116942019-09-03 23:10:20 +0900609 private boolean hasPhyBridge(OpenstackNode osNode, String bridgeName) {
610 BridgeConfig bridgeConfig = deviceService.getDevice(osNode.ovsdb()).as(BridgeConfig.class);
611 return bridgeConfig.getBridges().stream().anyMatch(br -> br.name().equals(bridgeName));
612 }
613
614 private boolean hasPhyPatchPort(OpenstackNode osNode, String patchPortName) {
615 List<Port> ports = deviceService.getPorts(osNode.intgBridge());
616 return ports.stream().anyMatch(p -> p.annotations().value(PORT_NAME).equals(patchPortName));
617 }
618
619 private boolean hasPhyIntf(OpenstackNode osNode, String intfName) {
620 BridgeConfig bridgeConfig = deviceService.getDevice(osNode.ovsdb()).as(BridgeConfig.class);
621 return bridgeConfig.getPorts().stream().anyMatch(p -> p.annotations().value(PORT_NAME).equals(intfName));
622 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900623
624 private boolean initStateDone(OpenstackNode osNode) {
625 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
626 return false;
627 }
628
629 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
630 if (hasDpdkTunnelBridge(osNode)) {
631 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
632 }
633
Jian Li62116942019-09-03 23:10:20 +0900634 cleanPhysicalInterfaces(osNode);
635
Daniel Park4b24cec2018-11-28 19:21:25 +0900636 return initStateDone;
637 }
638
639 private boolean deviceCreatedStateDone(OpenstackNode osNode) {
640 if (osNode.dataIp() != null &&
641 !isIntfEnabled(osNode, VXLAN_TUNNEL)) {
642 return false;
643 }
644 if (osNode.dataIp() != null &&
645 !isIntfEnabled(osNode, GRE_TUNNEL)) {
646 return false;
647 }
648 if (osNode.dataIp() != null &&
649 !isIntfEnabled(osNode, GENEVE_TUNNEL)) {
650 return false;
651 }
652 if (osNode.vlanIntf() != null &&
653 !isIntfEnabled(osNode, osNode.vlanIntf())) {
654 return false;
655 }
656 if (osNode.type() == GATEWAY &&
657 !isIntfEnabled(osNode, osNode.uplinkPort())) {
658 return false;
659 }
660 if (osNode.dpdkConfig() != null &&
661 osNode.dpdkConfig().dpdkIntfs() != null &&
662 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
663 return false;
664 }
665
Jian Li62116942019-09-03 23:10:20 +0900666 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
667 if (phyIntf == null) {
668 return false;
669 }
670
671 String bridgeName = BRIDGE_PREFIX + phyIntf.network();
672 String patchPortName = structurePortName(
673 INTEGRATION_TO_PHYSICAL_PREFIX + phyIntf.network());
674
675 if (!(hasPhyBridge(osNode, bridgeName) &&
676 hasPhyPatchPort(osNode, patchPortName) &&
677 hasPhyIntf(osNode, phyIntf.intf()))) {
Daniel Park4b24cec2018-11-28 19:21:25 +0900678 return false;
679 }
680 }
681
682 return true;
683 }
684
Jian Li340165f2018-02-27 10:38:17 +0900685 /**
686 * Checks whether all requirements for this state are fulfilled or not.
687 *
688 * @param osNode openstack node
689 * @return true if all requirements are fulfilled, false otherwise
690 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900691 private boolean isCurrentStateDone(OpenstackNode osNode) {
692 switch (osNode.state()) {
693 case INIT:
Daniel Park4b24cec2018-11-28 19:21:25 +0900694 return initStateDone(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900695 case DEVICE_CREATED:
Daniel Park4b24cec2018-11-28 19:21:25 +0900696 return deviceCreatedStateDone(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900697 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900698 case INCOMPLETE:
699 // always return false
700 // run init CLI to re-trigger node bootstrap
701 return false;
702 default:
703 return true;
704 }
705 }
706
Jian Li40888bf2018-11-21 09:46:32 +0900707 private boolean isDpdkIntfsCreated(OpenstackNode osNode,
708 Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700709 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900710 if (client == null) {
711 log.info("Failed to get ovsdb client");
712 return false;
713 }
714
715 Set<OvsdbPort> ports = client.getPorts();
716
Jian Li5ecfd1a2018-12-10 11:41:03 +0900717 for (DpdkInterface dpdkIntf : dpdkInterfaces) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900718 Optional<OvsdbPort> port = ports.stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900719 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkIntf.intf()))
Daniel Parke2658ba2018-08-24 22:33:29 +0900720 .findAny();
721
722 if (!port.isPresent()) {
723 return false;
724 }
Jian Li5ecfd1a2018-12-10 11:41:03 +0900725 Interface intf = client.getInterface(dpdkIntf.intf());
Daniel Parke2658ba2018-08-24 22:33:29 +0900726 if (intf == null) {
727 return false;
728 }
729
730 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
731 if (mtu == null) {
732 return false;
733 }
734
735 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
736 if (option == null) {
737 return false;
738 }
739
Jian Li5ecfd1a2018-12-10 11:41:03 +0900740 if (!mtu.set().contains(dpdkIntf.mtu().intValue()) ||
741 !option.toString().contains(dpdkIntf.pciAddress())) {
Jian Li40888bf2018-11-21 09:46:32 +0900742 log.trace("The dpdk interface {} was created but mtu or " +
743 "pci address is different from the config.");
Daniel Parke2658ba2018-08-24 22:33:29 +0900744 return false;
745 }
746 }
747 return true;
748 }
749
Jian Li340165f2018-02-27 10:38:17 +0900750 /**
751 * Configures the openstack node with new state.
752 *
753 * @param osNode openstack node
754 * @param newState a new state
755 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900756 private void setState(OpenstackNode osNode, NodeState newState) {
757 if (osNode.state() == newState) {
758 return;
759 }
760 OpenstackNode updated = osNode.updateState(newState);
761 osNodeAdminService.updateNode(updated);
762 log.info("Changed {} state: {}", osNode.hostname(), newState);
763 }
764
Jian Li340165f2018-02-27 10:38:17 +0900765 /**
766 * Bootstraps a new openstack node.
767 *
768 * @param osNode openstack node
769 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900770 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900771 if (osNode.type() == CONTROLLER) {
772 if (osNode.state() == INIT && checkEndpoint(osNode)) {
773 setState(osNode, COMPLETE);
774 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900775 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900776 if (isCurrentStateDone(osNode)) {
777 setState(osNode, osNode.state().nextState());
778 } else {
Jian Li97482c12018-07-03 01:08:23 +0900779 log.trace("Processing {} state for {}", osNode.state(),
780 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900781 osNode.state().process(this, osNode);
782 }
783 }
784 }
785
Daniel Park5a6a7102018-09-06 23:58:33 +0900786 private void removeVlanInterface(OpenstackNode osNode) {
787 if (osNode.vlanIntf() != null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900788 Optional<DpdkInterface> dpdkIntf =
789 dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900790
Jian Li5ecfd1a2018-12-10 11:41:03 +0900791 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900792 }
793 }
794
Jian Li5ecfd1a2018-12-10 11:41:03 +0900795 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode,
796 String intf) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900797 return osNode.dpdkConfig() == null ? Optional.empty() :
798 osNode.dpdkConfig().dpdkIntfs().stream()
799 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
800 .findAny();
801 }
802
803 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
804 String intfName,
805 Optional<DpdkInterface> dpdkInterface) {
806 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700807 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900808 ovsdbController, false);
809 } else {
810 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
811 false);
812 }
813 }
814
Jian Li51b844c2018-05-31 10:59:03 +0900815 /**
816 * Checks the validity of the given endpoint.
817 *
818 * @param osNode gateway node
819 * @return validity result
820 */
821 private boolean checkEndpoint(OpenstackNode osNode) {
822 if (osNode == null) {
823 log.warn("Keystone auth info has not been configured. " +
824 "Please specify auth info via network-cfg.json.");
825 return false;
826 }
827
828 OSClient client = getConnectedClient(osNode);
829
830 if (client == null) {
831 return false;
832 } else {
833 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900834 }
835 }
836
Jian Li340165f2018-02-27 10:38:17 +0900837 /**
Jian Li97482c12018-07-03 01:08:23 +0900838 * Extracts properties from the component configuration context.
839 *
840 * @param context the component context
841 */
842 private void readComponentConfiguration(ComponentContext context) {
843 Dictionary<?, ?> properties = context.getProperties();
844
845 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
846 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700847 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
848 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900849 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700850 ovsdbPortNum = ovsdbPortConfigured;
851 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900852 }
853
854 Boolean autoRecoveryConfigured =
855 getBooleanProperty(properties, AUTO_RECOVERY);
856 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700857 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900858 log.info("Auto recovery flag is NOT " +
859 "configured, default value is {}", autoRecovery);
860 } else {
861 autoRecovery = autoRecoveryConfigured;
862 log.info("Configured. Auto recovery flag is {}", autoRecovery);
863 }
864 }
865
866 /**
Jian Li340165f2018-02-27 10:38:17 +0900867 * An internal OVSDB listener. This listener is used for listening the
868 * network facing events from OVSDB device. If a new OVSDB device is detected,
869 * ONOS tries to bootstrap the openstack node.
870 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900871 private class InternalOvsdbListener implements DeviceListener {
872
873 @Override
874 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900875 return event.subject().type() == Device.Type.CONTROLLER;
876 }
877
878 private boolean isRelevantHelper() {
879 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900880 }
881
882 @Override
883 public void event(DeviceEvent event) {
884 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900885
886 switch (event.type()) {
887 case DEVICE_AVAILABILITY_CHANGED:
888 case DEVICE_ADDED:
889 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900890 if (!isRelevantHelper()) {
891 return;
892 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900893 processDeviceAddedOfOvsdbDevice(osNodeService.node(device.id()), device);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900894 });
895 break;
896 case PORT_ADDED:
897 case PORT_REMOVED:
898 case DEVICE_REMOVED:
899 default:
900 // do nothing
901 break;
902 }
903 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900904
905 private void processDeviceAddedOfOvsdbDevice(OpenstackNode osNode, Device device) {
906 if (osNode == null || osNode.type() == CONTROLLER) {
907 return;
908 }
909
910 if (deviceService.isAvailable(device.id())) {
911 log.debug("OVSDB {} detected", device.id());
912 bootstrapNode(osNode);
913 }
914 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900915 }
916
Jian Li340165f2018-02-27 10:38:17 +0900917 /**
918 * An internal integration bridge listener. This listener is used for
919 * listening the events from integration bridge. To listen the events from
920 * other types of bridge such as provider bridge or tunnel bridge, we need
921 * to augment OpenstackNodeService.node() method.
922 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900923 private class InternalBridgeListener implements DeviceListener {
924
925 @Override
926 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900927 return event.subject().type() == Device.Type.SWITCH;
928 }
929
930 private boolean isRelevantHelper() {
931 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900932 }
933
934 @Override
935 public void event(DeviceEvent event) {
936 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900937
938 switch (event.type()) {
939 case DEVICE_AVAILABILITY_CHANGED:
940 case DEVICE_ADDED:
941 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900942 if (!isRelevantHelper()) {
943 return;
944 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900945 processDeviceAddedOfBridge(osNodeService.node(device.id()), device);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900946 });
947 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900948 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900949 case PORT_ADDED:
950 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900951 if (!isRelevantHelper()) {
952 return;
953 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900954 processPortAddedOfBridge(osNodeService.node(device.id()), event.port());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900955 });
956 break;
957 case PORT_REMOVED:
958 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900959 if (!isRelevantHelper()) {
960 return;
961 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900962 processPortRemovedOfBridge(osNodeService.node(device.id()), event.port());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900963 });
964 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900965 case DEVICE_REMOVED:
966 default:
967 // do nothing
968 break;
969 }
970 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900971
Daniel Park4b24cec2018-11-28 19:21:25 +0900972 private void processDeviceAddedOfBridge(OpenstackNode osNode, Device device) {
Jian Lie6312162018-03-21 21:41:00 +0900973
Daniel Park4b24cec2018-11-28 19:21:25 +0900974 if (osNode == null || osNode.type() == CONTROLLER) {
975 return;
976 }
977
978 if (deviceService.isAvailable(device.id())) {
979 log.debug("Integration bridge created on {}", osNode.hostname());
980 bootstrapNode(osNode);
981 } else if (osNode.state() == COMPLETE) {
982 log.info("Device {} disconnected", device.id());
983 setState(osNode, INCOMPLETE);
984 }
985
986 if (autoRecovery) {
987 if (osNode.state() == INCOMPLETE ||
988 osNode.state() == DEVICE_CREATED) {
989 log.info("Device {} is reconnected", device.id());
990 osNodeAdminService.updateNode(
991 osNode.updateState(NodeState.INIT));
992 }
993 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900994 }
Daniel Park4b24cec2018-11-28 19:21:25 +0900995
996 private void processPortAddedOfBridge(OpenstackNode osNode, Port port) {
997 if (osNode == null || osNode.type() == CONTROLLER) {
998 return;
999 }
1000
1001 String portName = port.annotations().value(PORT_NAME);
1002 if (osNode.state() == DEVICE_CREATED && (
1003 Objects.equals(portName, VXLAN_TUNNEL) ||
1004 Objects.equals(portName, GRE_TUNNEL) ||
1005 Objects.equals(portName, GENEVE_TUNNEL) ||
1006 Objects.equals(portName, osNode.vlanIntf()) ||
1007 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Li62116942019-09-03 23:10:20 +09001008 containsPatchPort(osNode, portName)) ||
Daniel Park4b24cec2018-11-28 19:21:25 +09001009 containsDpdkIntfs(osNode, portName)) {
1010 log.info("Interface {} added or updated to {}",
1011 portName, osNode.intgBridge());
1012 bootstrapNode(osNode);
1013 }
1014 }
1015
1016 private void processPortRemovedOfBridge(OpenstackNode osNode, Port port) {
1017 if (osNode == null || osNode.type() == CONTROLLER) {
1018 return;
1019 }
1020
1021 String portName = port.annotations().value(PORT_NAME);
1022 if (osNode.state() == COMPLETE && (
1023 Objects.equals(portName, VXLAN_TUNNEL) ||
1024 Objects.equals(portName, GRE_TUNNEL) ||
1025 Objects.equals(portName, GENEVE_TUNNEL) ||
1026 Objects.equals(portName, osNode.vlanIntf()) ||
1027 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Li62116942019-09-03 23:10:20 +09001028 containsPatchPort(osNode, portName)) ||
Daniel Park4b24cec2018-11-28 19:21:25 +09001029 containsDpdkIntfs(osNode, portName)) {
1030 log.warn("Interface {} removed from {}",
1031 portName, osNode.intgBridge());
1032 setState(osNode, INCOMPLETE);
1033 }
1034 }
1035
Daniel Park4b24cec2018-11-28 19:21:25 +09001036 /**
Jian Li62116942019-09-03 23:10:20 +09001037 * Checks whether the openstack node contains the given patch port.
Daniel Park4b24cec2018-11-28 19:21:25 +09001038 *
Jian Li62116942019-09-03 23:10:20 +09001039 * @param osNode openstack node
1040 * @param portName patch port name
1041 * @return true if openstack node contains the given patch port,
1042 * false otherwise
Daniel Park4b24cec2018-11-28 19:21:25 +09001043 */
Jian Li62116942019-09-03 23:10:20 +09001044 private boolean containsPatchPort(OpenstackNode osNode, String portName) {
Daniel Park4b24cec2018-11-28 19:21:25 +09001045 return osNode.phyIntfs().stream()
Jian Li62116942019-09-03 23:10:20 +09001046 .anyMatch(pi -> structurePortName(INTEGRATION_TO_PHYSICAL_PREFIX
1047 + pi.network()).equals(portName));
Daniel Park4b24cec2018-11-28 19:21:25 +09001048 }
1049
1050 /**
1051 * Checks whether the openstack node contains the given dpdk interface.
1052 *
1053 * @param osNode openstack node
1054 * @param portName dpdk interface
1055 * @return true if openstack node contains the given dpdk interface,
1056 * false otherwise
1057 */
1058 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
1059 if (osNode.dpdkConfig() == null) {
1060 return false;
1061 }
1062 return osNode.dpdkConfig().dpdkIntfs().stream()
1063 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
1064 }
Jian Lie6312162018-03-21 21:41:00 +09001065 }
1066
1067 /**
Jian Li340165f2018-02-27 10:38:17 +09001068 * An internal openstack node listener.
1069 * The notification is triggered by OpenstackNodeStore.
1070 */
Hyunsun Moon0d457362017-06-27 17:19:41 +09001071 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
1072
Jian Li40888bf2018-11-21 09:46:32 +09001073 private boolean isRelevantHelper() {
1074 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +09001075 }
1076
1077 @Override
1078 public void event(OpenstackNodeEvent event) {
1079 switch (event.type()) {
1080 case OPENSTACK_NODE_CREATED:
1081 case OPENSTACK_NODE_UPDATED:
Jian Li40888bf2018-11-21 09:46:32 +09001082 eventExecutor.execute(() -> {
1083
1084 if (!isRelevantHelper()) {
1085 return;
1086 }
1087
1088 bootstrapNode(event.subject());
1089 });
Hyunsun Moon0d457362017-06-27 17:19:41 +09001090 break;
1091 case OPENSTACK_NODE_REMOVED:
Jian Li40888bf2018-11-21 09:46:32 +09001092 eventExecutor.execute(() -> {
1093
1094 if (!isRelevantHelper()) {
1095 return;
1096 }
Jian Li40888bf2018-11-21 09:46:32 +09001097 processOpenstackNodeRemoved(event.subject());
1098 });
Hyunsun Moon0d457362017-06-27 17:19:41 +09001099 break;
Jian Li40888bf2018-11-21 09:46:32 +09001100 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +09001101 default:
1102 break;
1103 }
1104 }
Daniel Park4b24cec2018-11-28 19:21:25 +09001105
1106 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
1107 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
1108 if (client == null) {
1109 log.info("Failed to get ovsdb client");
1110 return;
1111 }
1112
Jian Li62116942019-09-03 23:10:20 +09001113 // unprovision physical interfaces from the node
1114 // this procedure includes detaching physical port from physical bridge,
1115 // remove patch ports from br-int, removing physical bridge
1116 unprovisionPhysicalInterfaces(osNode);
Daniel Park4b24cec2018-11-28 19:21:25 +09001117
1118 //delete vlan interface from the node
1119 removeVlanInterface(osNode);
1120
1121 //delete dpdk interfaces from the node
1122 if (osNode.dpdkConfig() != null) {
1123 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
1124 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
1125 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum,
1126 ovsdbController, false);
1127 }
1128 });
1129 }
1130
1131 //delete tunnel bridge from the node
1132 if (hasDpdkTunnelBridge(osNode)) {
1133 client.dropBridge(TUNNEL_BRIDGE);
1134 }
1135
1136 //delete integration bridge from the node
1137 client.dropBridge(INTEGRATION_BRIDGE);
1138
1139 //disconnect ovsdb
1140 client.disconnect();
1141 }
Hyunsun Moon0d457362017-06-27 17:19:41 +09001142 }
1143}