blob: dab1fc116de43ffec6c0eb14ccebc5f4e1ff99d6 [file] [log] [blame]
Hyunsun Moon0d457362017-06-27 17:19:41 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon0d457362017-06-27 17:19:41 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknode.impl;
17
Ray Milkey86ad7bb2018-09-27 12:32:28 -070018import com.google.common.collect.Lists;
Hyunsun Moon0d457362017-06-27 17:19:41 +090019import org.onlab.packet.IpAddress;
20import org.onlab.util.Tools;
21import org.onosproject.cfg.ComponentConfigService;
22import org.onosproject.cluster.ClusterService;
23import org.onosproject.cluster.ControllerNode;
24import org.onosproject.cluster.LeadershipService;
25import org.onosproject.cluster.NodeId;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090028import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Port;
31import org.onosproject.net.behaviour.BridgeConfig;
32import org.onosproject.net.behaviour.BridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090033import org.onosproject.net.behaviour.ControllerInfo;
34import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090035import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090036import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon0d457362017-06-27 17:19:41 +090037import org.onosproject.net.behaviour.TunnelDescription;
38import org.onosproject.net.behaviour.TunnelEndPoints;
39import org.onosproject.net.behaviour.TunnelKeys;
40import org.onosproject.net.device.DeviceAdminService;
41import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
43import org.onosproject.net.device.DeviceService;
Daniel Parke2658ba2018-08-24 22:33:29 +090044import org.onosproject.openstacknode.api.DpdkInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090045import org.onosproject.openstacknode.api.NodeState;
46import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090047import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
48import org.onosproject.openstacknode.api.OpenstackNodeEvent;
49import org.onosproject.openstacknode.api.OpenstackNodeHandler;
50import org.onosproject.openstacknode.api.OpenstackNodeListener;
51import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090052import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Daniel Parke2658ba2018-08-24 22:33:29 +090053import org.onosproject.ovsdb.controller.OvsdbClientService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090054import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Parke2658ba2018-08-24 22:33:29 +090055import org.onosproject.ovsdb.controller.OvsdbPort;
56import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
57import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
58import org.onosproject.ovsdb.rfc.table.Interface;
Jian Li51b844c2018-05-31 10:59:03 +090059import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090060import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Modified;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import org.slf4j.Logger;
68
Daniel Parke2658ba2018-08-24 22:33:29 +090069import java.util.Collection;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import java.util.Dictionary;
71import java.util.List;
72import java.util.Objects;
Daniel Parke2658ba2018-08-24 22:33:29 +090073import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090074import java.util.Set;
75import java.util.concurrent.ExecutorService;
76import java.util.stream.Collectors;
77
Hyunsun Moon0d457362017-06-27 17:19:41 +090078import static java.util.concurrent.Executors.newSingleThreadExecutor;
79import static org.onlab.packet.TpPort.tpPort;
80import static org.onlab.util.Tools.groupedThreads;
81import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li5afbea42018-02-28 10:37:03 +090082import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
83import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090084import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090085import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090086import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
87import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
88import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090089import static org.onosproject.openstacknode.api.NodeState.INIT;
90import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090091import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
92import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Ray Milkey8e406512018-10-24 15:56:50 -070093import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
94import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
95import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
96import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +090097import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
98import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +090099import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +0900100import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +0900101import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900102import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103import static org.slf4j.LoggerFactory.getLogger;
104
105/**
106 * Service bootstraps openstack node based on its type.
107 */
Ray Milkey8e406512018-10-24 15:56:50 -0700108@Component(immediate = true,
109 property = {
110 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
111 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
112 }
113)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900114public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
115
Jian Li5afbea42018-02-28 10:37:03 +0900116 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900117
Hyunsun Moon0d457362017-06-27 17:19:41 +0900118 private static final String DEFAULT_OF_PROTO = "tcp";
119 private static final int DEFAULT_OFPORT = 6653;
120 private static final int DPID_BEGIN = 3;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900123 protected CoreService coreService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126 protected LeadershipService leadershipService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 protected ClusterService clusterService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 protected DeviceService deviceService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900135 protected DeviceAdminService deviceAdminService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900138 protected OvsdbController ovsdbController;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900141 protected OpenstackNodeService osNodeService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900144 protected OpenstackNodeAdminService osNodeAdminService;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900147 protected ComponentConfigService componentConfigService;
148
Ray Milkey8e406512018-10-24 15:56:50 -0700149 /** OVSDB server listen port. */
150 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900151
Jian Li5ecfd1a2018-12-10 11:41:03 +0900152 /** A flag which indicates whether auto-recover openstack node status on
153 * switch reconnecting event. */
Ray Milkey8e406512018-10-24 15:56:50 -0700154 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900155
Hyunsun Moon0d457362017-06-27 17:19:41 +0900156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
158
159 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
160 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900161 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
162
163 private ApplicationId appId;
164 private NodeId localNode;
165
166 @Activate
167 protected void activate() {
168 appId = coreService.getAppId(APP_ID);
169 localNode = clusterService.getLocalNode().id();
170
171 componentConfigService.registerProperties(getClass());
172 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900173 deviceService.addListener(ovsdbListener);
174 deviceService.addListener(bridgeListener);
175 osNodeService.addListener(osNodeListener);
176
177 log.info("Started");
178 }
179
180 @Deactivate
181 protected void deactivate() {
182 osNodeService.removeListener(osNodeListener);
183 deviceService.removeListener(bridgeListener);
184 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900185 componentConfigService.unregisterProperties(getClass(), false);
186 leadershipService.withdraw(appId.name());
187 eventExecutor.shutdown();
188
189 log.info("Stopped");
190 }
191
192 @Modified
193 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900194 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900195
196 log.info("Modified");
197 }
198
199 @Override
200 public void processInitState(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700201 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
202 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900203 return;
204 }
205 if (!deviceService.isAvailable(osNode.intgBridge())) {
206 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
207 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900208 if (hasDpdkTunnelBridge(osNode)) {
209 createDpdkTunnelBridge(osNode);
210 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900211 }
212
213 @Override
214 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900215 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700216 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
217 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
daniel parkb18424c2018-02-05 15:43:43 +0900218 return;
219 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900220
daniel parkb18424c2018-02-05 15:43:43 +0900221 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900222 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900223 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900224 }
225
226 if (osNode.dataIp() != null &&
227 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
228 createTunnelInterface(osNode);
229 }
230
Daniel Parke2658ba2018-08-24 22:33:29 +0900231 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900232 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900233 .filter(dpdkintf -> dpdkintf.deviceName().equals(TUNNEL_BRIDGE))
234 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
235 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Park5a6a7102018-09-06 23:58:33 +0900236
237 osNode.dpdkConfig().dpdkIntfs().stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900238 .filter(dpdkintf -> dpdkintf.deviceName().equals(INTEGRATION_BRIDGE))
239 .forEach(dpdkintf -> addOrRemoveDpdkInterface(
240 osNode, dpdkintf, ovsdbPortNum, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900241 }
242
Jian Lie6312162018-03-21 21:41:00 +0900243 osNode.phyIntfs().forEach(i -> {
244 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900245 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900246 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900247 }
248 });
249
Daniel Park5a6a7102018-09-06 23:58:33 +0900250 if (osNode.vlanIntf() != null &&
251 !isIntfEnabled(osNode, osNode.vlanIntf())) {
252 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900253 osNode.vlanIntf(), deviceService, true);
Daniel Park5a6a7102018-09-06 23:58:33 +0900254 }
daniel parkb18424c2018-02-05 15:43:43 +0900255 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900256 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900257 }
258 }
259
260 @Override
261 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900262 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900263 }
264
265 @Override
266 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900267 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900268 }
269
Daniel Parke2658ba2018-08-24 22:33:29 +0900270 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
271 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
272 return osNode.dpdkConfig().dpdkIntfs().stream()
273 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
274 }
275 return false;
276 }
277
278 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
279
Ray Milkey8e406512018-10-24 15:56:50 -0700280 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900281 if (client == null) {
282 log.info("Failed to get ovsdb client");
283 return false;
284 }
285
286 return client.getBridges().stream()
287 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
288 }
289
Jian Li340165f2018-02-27 10:38:17 +0900290 /**
Jian Li340165f2018-02-27 10:38:17 +0900291 * Creates a bridge with a given name on a given openstack node.
292 *
293 * @param osNode openstack node
294 * @param bridgeName bridge name
295 * @param deviceId device identifier
296 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900297 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
298 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900299
Jian Li789fadb2018-07-10 13:59:47 +0900300 List<ControllerInfo> controllers;
301
302 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
303 controllers = (List<ControllerInfo>) osNode.controllers();
304 } else {
305 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306 .map(ControllerNode::ip)
307 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900308
Jian Li789fadb2018-07-10 13:59:47 +0900309 controllers = controllerIps.stream()
310 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
311 .collect(Collectors.toList());
312 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900313
314 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900315
Daniel Park92abf312018-08-08 17:01:35 +0900316 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900317 .name(bridgeName)
318 .failMode(BridgeDescription.FailMode.SECURE)
319 .datapathId(dpid)
320 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900321 .controllers(controllers);
322
323 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900324 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900325 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900326
327 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900328 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900329 }
330
Daniel Parke2658ba2018-08-24 22:33:29 +0900331 private void createDpdkTunnelBridge(OpenstackNode osNode) {
332 Device device = deviceService.getDevice(osNode.ovsdb());
333
334 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
335 .name(TUNNEL_BRIDGE)
336 .datapathType(NETDEV.name().toLowerCase());
337
338 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
339 bridgeConfig.addBridge(builder.build());
340 }
341
Jian Li340165f2018-02-27 10:38:17 +0900342 /**
Jian Li340165f2018-02-27 10:38:17 +0900343 * Creates a tunnel interface in a given openstack node.
344 *
345 * @param osNode openstack node
346 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900347 private void createTunnelInterface(OpenstackNode osNode) {
348 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
349 return;
350 }
351
352 Device device = deviceService.getDevice(osNode.ovsdb());
353 if (device == null || !device.is(InterfaceConfig.class)) {
354 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
355 return;
356 }
357
358 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
359 .deviceId(INTEGRATION_BRIDGE)
360 .ifaceName(DEFAULT_TUNNEL)
361 .type(TunnelDescription.Type.VXLAN)
362 .remote(TunnelEndPoints.flowTunnelEndpoint())
363 .key(TunnelKeys.flowTunnelKey())
364 .build();
365
366 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
367 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
368 }
369
Jian Li340165f2018-02-27 10:38:17 +0900370 /**
Jian Li5ecfd1a2018-12-10 11:41:03 +0900371 * Checks whether a given network interface in a given openstack node
372 * is enabled or not.
Jian Li340165f2018-02-27 10:38:17 +0900373 *
374 * @param osNode openstack node
375 * @param intf network interface name
376 * @return true if the given interface is enabled, false otherwise
377 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900378 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900379 return deviceService.isAvailable(osNode.intgBridge()) &&
380 deviceService.getPorts(osNode.intgBridge()).stream()
381 .anyMatch(port -> Objects.equals(
382 port.annotations().value(PORT_NAME), intf) &&
383 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900384 }
385
Jian Li340165f2018-02-27 10:38:17 +0900386 /**
387 * Checks whether all requirements for this state are fulfilled or not.
388 *
389 * @param osNode openstack node
390 * @return true if all requirements are fulfilled, false otherwise
391 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900392 private boolean isCurrentStateDone(OpenstackNode osNode) {
393 switch (osNode.state()) {
394 case INIT:
Jian Li5ecfd1a2018-12-10 11:41:03 +0900395 if (!isOvsdbConnected(osNode, ovsdbPortNum,
396 ovsdbController, deviceService)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900397 return false;
398 }
399
Daniel Parke2658ba2018-08-24 22:33:29 +0900400 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
401 if (hasDpdkTunnelBridge(osNode)) {
402 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
403 }
404 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900405 case DEVICE_CREATED:
406 if (osNode.dataIp() != null &&
407 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
408 return false;
409 }
410 if (osNode.vlanIntf() != null &&
411 !isIntfEnabled(osNode, osNode.vlanIntf())) {
412 return false;
413 }
daniel parkb18424c2018-02-05 15:43:43 +0900414 if (osNode.type() == GATEWAY &&
415 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900416 return false;
417 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900418 if (osNode.dpdkConfig() != null &&
419 osNode.dpdkConfig().dpdkIntfs() != null &&
420 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
421 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900422 }
Jian Lie6312162018-03-21 21:41:00 +0900423
424 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
425 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
426 return false;
427 }
428 }
429
Hyunsun Moon0d457362017-06-27 17:19:41 +0900430 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900431 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900432 case INCOMPLETE:
433 // always return false
434 // run init CLI to re-trigger node bootstrap
435 return false;
436 default:
437 return true;
438 }
439 }
440
Jian Li40888bf2018-11-21 09:46:32 +0900441 private boolean isDpdkIntfsCreated(OpenstackNode osNode,
442 Collection<DpdkInterface> dpdkInterfaces) {
Ray Milkey8e406512018-10-24 15:56:50 -0700443 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Parke2658ba2018-08-24 22:33:29 +0900444 if (client == null) {
445 log.info("Failed to get ovsdb client");
446 return false;
447 }
448
449 Set<OvsdbPort> ports = client.getPorts();
450
Jian Li5ecfd1a2018-12-10 11:41:03 +0900451 for (DpdkInterface dpdkIntf : dpdkInterfaces) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900452 Optional<OvsdbPort> port = ports.stream()
Jian Li5ecfd1a2018-12-10 11:41:03 +0900453 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkIntf.intf()))
Daniel Parke2658ba2018-08-24 22:33:29 +0900454 .findAny();
455
456 if (!port.isPresent()) {
457 return false;
458 }
Jian Li5ecfd1a2018-12-10 11:41:03 +0900459 Interface intf = client.getInterface(dpdkIntf.intf());
Daniel Parke2658ba2018-08-24 22:33:29 +0900460 if (intf == null) {
461 return false;
462 }
463
464 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
465 if (mtu == null) {
466 return false;
467 }
468
469 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
470 if (option == null) {
471 return false;
472 }
473
Jian Li5ecfd1a2018-12-10 11:41:03 +0900474 if (!mtu.set().contains(dpdkIntf.mtu().intValue()) ||
475 !option.toString().contains(dpdkIntf.pciAddress())) {
Jian Li40888bf2018-11-21 09:46:32 +0900476 log.trace("The dpdk interface {} was created but mtu or " +
477 "pci address is different from the config.");
Daniel Parke2658ba2018-08-24 22:33:29 +0900478 return false;
479 }
480 }
481 return true;
482 }
483
Jian Li340165f2018-02-27 10:38:17 +0900484 /**
485 * Configures the openstack node with new state.
486 *
487 * @param osNode openstack node
488 * @param newState a new state
489 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900490 private void setState(OpenstackNode osNode, NodeState newState) {
491 if (osNode.state() == newState) {
492 return;
493 }
494 OpenstackNode updated = osNode.updateState(newState);
495 osNodeAdminService.updateNode(updated);
496 log.info("Changed {} state: {}", osNode.hostname(), newState);
497 }
498
Jian Li340165f2018-02-27 10:38:17 +0900499 /**
500 * Bootstraps a new openstack node.
501 *
502 * @param osNode openstack node
503 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900504 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900505 if (osNode.type() == CONTROLLER) {
506 if (osNode.state() == INIT && checkEndpoint(osNode)) {
507 setState(osNode, COMPLETE);
508 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900509 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900510 if (isCurrentStateDone(osNode)) {
511 setState(osNode, osNode.state().nextState());
512 } else {
Jian Li97482c12018-07-03 01:08:23 +0900513 log.trace("Processing {} state for {}", osNode.state(),
514 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900515 osNode.state().process(this, osNode);
516 }
517 }
518 }
519
Daniel Park5a6a7102018-09-06 23:58:33 +0900520 private void removeVlanInterface(OpenstackNode osNode) {
521 if (osNode.vlanIntf() != null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900522 Optional<DpdkInterface> dpdkIntf =
523 dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900524
Jian Li5ecfd1a2018-12-10 11:41:03 +0900525 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900526 }
527 }
528
529 private void removePhysicalInterface(OpenstackNode osNode) {
530 osNode.phyIntfs().forEach(phyIntf -> {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900531 Optional<DpdkInterface> dpdkIntf = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
Daniel Park5a6a7102018-09-06 23:58:33 +0900532
Jian Li5ecfd1a2018-12-10 11:41:03 +0900533 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkIntf);
Daniel Park5a6a7102018-09-06 23:58:33 +0900534 });
535 }
536
Jian Li5ecfd1a2018-12-10 11:41:03 +0900537 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode,
538 String intf) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900539 return osNode.dpdkConfig() == null ? Optional.empty() :
540 osNode.dpdkConfig().dpdkIntfs().stream()
541 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
542 .findAny();
543 }
544
545 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
546 String intfName,
547 Optional<DpdkInterface> dpdkInterface) {
548 if (dpdkInterface.isPresent()) {
Ray Milkey8e406512018-10-24 15:56:50 -0700549 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPortNum,
Daniel Park5a6a7102018-09-06 23:58:33 +0900550 ovsdbController, false);
551 } else {
552 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
553 false);
554 }
555 }
556
557 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700558 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPortNum, ovsdbController);
Daniel Park489645c2018-10-24 11:34:22 +0900559 if (client == null) {
560 log.info("Failed to get ovsdb client");
561 return;
562 }
563
Daniel Park5a6a7102018-09-06 23:58:33 +0900564 //delete physical interfaces from the node
565 removePhysicalInterface(osNode);
566
567 //delete vlan interface from the node
568 removeVlanInterface(osNode);
569
570 //delete dpdk interfaces from the node
571 if (osNode.dpdkConfig() != null) {
572 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
573 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
Jian Li40888bf2018-11-21 09:46:32 +0900574 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPortNum,
575 ovsdbController, false);
Daniel Park5a6a7102018-09-06 23:58:33 +0900576 }
577 });
578 }
Daniel Park489645c2018-10-24 11:34:22 +0900579
580 //delete tunnel bridge from the node
581 if (hasDpdkTunnelBridge(osNode)) {
582 client.dropBridge(TUNNEL_BRIDGE);
583 }
584
585 //delete integration bridge from the node
586 client.dropBridge(INTEGRATION_BRIDGE);
587
588 //disconnect ovsdb
589 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900590 }
591
Jian Li51b844c2018-05-31 10:59:03 +0900592 /**
593 * Checks the validity of the given endpoint.
594 *
595 * @param osNode gateway node
596 * @return validity result
597 */
598 private boolean checkEndpoint(OpenstackNode osNode) {
599 if (osNode == null) {
600 log.warn("Keystone auth info has not been configured. " +
601 "Please specify auth info via network-cfg.json.");
602 return false;
603 }
604
605 OSClient client = getConnectedClient(osNode);
606
607 if (client == null) {
608 return false;
609 } else {
610 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900611 }
612 }
613
Jian Li340165f2018-02-27 10:38:17 +0900614 /**
Jian Li97482c12018-07-03 01:08:23 +0900615 * Extracts properties from the component configuration context.
616 *
617 * @param context the component context
618 */
619 private void readComponentConfiguration(ComponentContext context) {
620 Dictionary<?, ?> properties = context.getProperties();
621
622 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
623 if (ovsdbPortConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700624 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
625 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900626 } else {
Ray Milkey8e406512018-10-24 15:56:50 -0700627 ovsdbPortNum = ovsdbPortConfigured;
628 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
Jian Li97482c12018-07-03 01:08:23 +0900629 }
630
631 Boolean autoRecoveryConfigured =
632 getBooleanProperty(properties, AUTO_RECOVERY);
633 if (autoRecoveryConfigured == null) {
Ray Milkey8e406512018-10-24 15:56:50 -0700634 autoRecovery = AUTO_RECOVERY_DEFAULT;
Jian Li97482c12018-07-03 01:08:23 +0900635 log.info("Auto recovery flag is NOT " +
636 "configured, default value is {}", autoRecovery);
637 } else {
638 autoRecovery = autoRecoveryConfigured;
639 log.info("Configured. Auto recovery flag is {}", autoRecovery);
640 }
641 }
642
643 /**
Jian Li340165f2018-02-27 10:38:17 +0900644 * An internal OVSDB listener. This listener is used for listening the
645 * network facing events from OVSDB device. If a new OVSDB device is detected,
646 * ONOS tries to bootstrap the openstack node.
647 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900648 private class InternalOvsdbListener implements DeviceListener {
649
650 @Override
651 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900652 return event.subject().type() == Device.Type.CONTROLLER;
653 }
654
655 private boolean isRelevantHelper() {
656 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900657 }
658
659 @Override
660 public void event(DeviceEvent event) {
661 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900662
663 switch (event.type()) {
664 case DEVICE_AVAILABILITY_CHANGED:
665 case DEVICE_ADDED:
666 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900667
668 if (!isRelevantHelper()) {
669 return;
670 }
671
Jian Licab41762018-11-06 12:30:08 +0900672 OpenstackNode osNode = osNodeService.node(device.id());
673
674 if (osNode == null || osNode.type() == CONTROLLER) {
675 return;
676 }
677
Hyunsun Moon0d457362017-06-27 17:19:41 +0900678 if (deviceService.isAvailable(device.id())) {
679 log.debug("OVSDB {} detected", device.id());
680 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900681 }
682 });
683 break;
684 case PORT_ADDED:
685 case PORT_REMOVED:
686 case DEVICE_REMOVED:
687 default:
688 // do nothing
689 break;
690 }
691 }
692 }
693
Jian Li340165f2018-02-27 10:38:17 +0900694 /**
695 * An internal integration bridge listener. This listener is used for
696 * listening the events from integration bridge. To listen the events from
697 * other types of bridge such as provider bridge or tunnel bridge, we need
698 * to augment OpenstackNodeService.node() method.
699 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900700 private class InternalBridgeListener implements DeviceListener {
701
702 @Override
703 public boolean isRelevant(DeviceEvent event) {
Jian Li40888bf2018-11-21 09:46:32 +0900704 return event.subject().type() == Device.Type.SWITCH;
705 }
706
707 private boolean isRelevantHelper() {
708 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900709 }
710
711 @Override
712 public void event(DeviceEvent event) {
713 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900714
715 switch (event.type()) {
716 case DEVICE_AVAILABILITY_CHANGED:
717 case DEVICE_ADDED:
718 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900719
720 if (!isRelevantHelper()) {
721 return;
722 }
723
Jian Licab41762018-11-06 12:30:08 +0900724 OpenstackNode osNode = osNodeService.node(device.id());
725
726 if (osNode == null || osNode.type() == CONTROLLER) {
727 return;
728 }
729
Hyunsun Moon0d457362017-06-27 17:19:41 +0900730 if (deviceService.isAvailable(device.id())) {
731 log.debug("Integration bridge created on {}", osNode.hostname());
732 bootstrapNode(osNode);
733 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900734 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900735 setState(osNode, INCOMPLETE);
736 }
Jian Li97482c12018-07-03 01:08:23 +0900737
738 if (autoRecovery) {
739 if (osNode.state() == INCOMPLETE ||
740 osNode.state() == DEVICE_CREATED) {
741 log.info("Device {} is reconnected", device.id());
742 osNodeAdminService.updateNode(
743 osNode.updateState(NodeState.INIT));
744 }
745 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900746 });
747 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900748 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900749 case PORT_ADDED:
750 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900751
752 if (!isRelevantHelper()) {
753 return;
754 }
755
Jian Licab41762018-11-06 12:30:08 +0900756 OpenstackNode osNode = osNodeService.node(device.id());
757
758 if (osNode == null || osNode.type() == CONTROLLER) {
759 return;
760 }
761
Hyunsun Moon0d457362017-06-27 17:19:41 +0900762 Port port = event.port();
763 String portName = port.annotations().value(PORT_NAME);
764 if (osNode.state() == DEVICE_CREATED && (
765 Objects.equals(portName, DEFAULT_TUNNEL) ||
766 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900767 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900768 containsPhyIntf(osNode, portName)) ||
769 containsDpdkIntfs(osNode, portName)) {
770 log.info("Interface {} added or updated to {}",
771 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900772 bootstrapNode(osNode);
773 }
774 });
775 break;
776 case PORT_REMOVED:
777 eventExecutor.execute(() -> {
Jian Li40888bf2018-11-21 09:46:32 +0900778
779 if (!isRelevantHelper()) {
780 return;
781 }
782
Jian Licab41762018-11-06 12:30:08 +0900783 OpenstackNode osNode = osNodeService.node(device.id());
784
785 if (osNode == null || osNode.type() == CONTROLLER) {
786 return;
787 }
788
Hyunsun Moon0d457362017-06-27 17:19:41 +0900789 Port port = event.port();
790 String portName = port.annotations().value(PORT_NAME);
791 if (osNode.state() == COMPLETE && (
792 Objects.equals(portName, DEFAULT_TUNNEL) ||
793 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900794 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900795 containsPhyIntf(osNode, portName)) ||
796 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900797 log.warn("Interface {} removed from {}",
798 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900799 setState(osNode, INCOMPLETE);
800 }
801 });
802 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900803 case DEVICE_REMOVED:
804 default:
805 // do nothing
806 break;
807 }
808 }
809 }
810
Jian Li340165f2018-02-27 10:38:17 +0900811 /**
Jian Lie6312162018-03-21 21:41:00 +0900812 * Checks whether the openstack node contains the given physical interface.
813 *
814 * @param osNode openstack node
815 * @param portName physical interface
816 * @return true if openstack node contains the given physical interface,
817 * false otherwise
818 */
819 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900820 return osNode.phyIntfs().stream()
821 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
822 }
Jian Lie6312162018-03-21 21:41:00 +0900823
Daniel Park5a6a7102018-09-06 23:58:33 +0900824 /**
825 * Checks whether the openstack node contains the given dpdk interface.
826 *
827 * @param osNode openstack node
828 * @param portName dpdk interface
829 * @return true if openstack node contains the given dpdk interface,
830 * false otherwise
831 */
832 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
833 if (osNode.dpdkConfig() == null) {
834 return false;
835 }
836 return osNode.dpdkConfig().dpdkIntfs().stream()
837 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900838 }
839
840 /**
Jian Li340165f2018-02-27 10:38:17 +0900841 * An internal openstack node listener.
842 * The notification is triggered by OpenstackNodeStore.
843 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900844 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
845
Jian Li40888bf2018-11-21 09:46:32 +0900846 private boolean isRelevantHelper() {
847 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900848 }
849
850 @Override
851 public void event(OpenstackNodeEvent event) {
852 switch (event.type()) {
853 case OPENSTACK_NODE_CREATED:
854 case OPENSTACK_NODE_UPDATED:
Jian Li40888bf2018-11-21 09:46:32 +0900855 eventExecutor.execute(() -> {
856
857 if (!isRelevantHelper()) {
858 return;
859 }
860
861 bootstrapNode(event.subject());
862 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900863 break;
864 case OPENSTACK_NODE_REMOVED:
Jian Li40888bf2018-11-21 09:46:32 +0900865 eventExecutor.execute(() -> {
866
867 if (!isRelevantHelper()) {
868 return;
869 }
870
871 processOpenstackNodeRemoved(event.subject());
872 });
Hyunsun Moon0d457362017-06-27 17:19:41 +0900873 break;
Jian Li40888bf2018-11-21 09:46:32 +0900874 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900875 default:
876 break;
877 }
878 }
879 }
880}