blob: 9a242bbdb1417bac671dea880e5ee6e10f9a237c [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;
Daniel Park5a6a7102018-09-06 23:58:33 +090093import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
94import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +090095import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090096import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +090097import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090098import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090099import static org.slf4j.LoggerFactory.getLogger;
100
101/**
102 * Service bootstraps openstack node based on its type.
103 */
104@Component(immediate = true)
105public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
106
Jian Li5afbea42018-02-28 10:37:03 +0900107 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900108
109 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900110 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900111 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900112 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900113 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900114 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900115 private static final int DPID_BEGIN = 3;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900118 protected CoreService coreService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900121 protected LeadershipService leadershipService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900124 protected ClusterService clusterService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900127 protected DeviceService deviceService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900130 protected DeviceAdminService deviceAdminService;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900133 protected OvsdbController ovsdbController;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900136 protected OpenstackNodeService osNodeService;
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900139 protected OpenstackNodeAdminService osNodeAdminService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900142 protected ComponentConfigService componentConfigService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 //@Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
145 // label = "OVSDB server listen port")
Hyunsun Moon0d457362017-06-27 17:19:41 +0900146 private int ovsdbPort = DEFAULT_OVSDB_PORT;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 //@Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
149 // label = "A flag which indicates whether auto-recover openstack " +
150 // "node status at the receiving of switch reconnecting event.")
Jian Li97482c12018-07-03 01:08:23 +0900151 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
152
Hyunsun Moon0d457362017-06-27 17:19:41 +0900153 private final ExecutorService eventExecutor = newSingleThreadExecutor(
154 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
155
156 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
157 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900158 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
159
160 private ApplicationId appId;
161 private NodeId localNode;
162
163 @Activate
164 protected void activate() {
165 appId = coreService.getAppId(APP_ID);
166 localNode = clusterService.getLocalNode().id();
167
168 componentConfigService.registerProperties(getClass());
169 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900170 deviceService.addListener(ovsdbListener);
171 deviceService.addListener(bridgeListener);
172 osNodeService.addListener(osNodeListener);
173
174 log.info("Started");
175 }
176
177 @Deactivate
178 protected void deactivate() {
179 osNodeService.removeListener(osNodeListener);
180 deviceService.removeListener(bridgeListener);
181 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900182 componentConfigService.unregisterProperties(getClass(), false);
183 leadershipService.withdraw(appId.name());
184 eventExecutor.shutdown();
185
186 log.info("Stopped");
187 }
188
189 @Modified
190 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900191 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900192
193 log.info("Modified");
194 }
195
196 @Override
197 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900198 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900199 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
200 return;
201 }
202 if (!deviceService.isAvailable(osNode.intgBridge())) {
203 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
204 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900205 if (hasDpdkTunnelBridge(osNode)) {
206 createDpdkTunnelBridge(osNode);
207 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900208 }
209
210 @Override
211 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900212 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900213 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900214 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
215 return;
216 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900217
daniel parkb18424c2018-02-05 15:43:43 +0900218 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900219 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900220 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900221 }
222
223 if (osNode.dataIp() != null &&
224 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
225 createTunnelInterface(osNode);
226 }
227
Daniel Parke2658ba2018-08-24 22:33:29 +0900228 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900229 osNode.dpdkConfig().dpdkIntfs().stream()
230 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(TUNNEL_BRIDGE))
231 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
232 osNode, dpdkInterface, ovsdbPort, ovsdbController, true));
233
234 osNode.dpdkConfig().dpdkIntfs().stream()
235 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(INTEGRATION_BRIDGE))
236 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
237 osNode, dpdkInterface, ovsdbPort, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900238 }
239
Jian Lie6312162018-03-21 21:41:00 +0900240 osNode.phyIntfs().forEach(i -> {
241 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900242 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900243 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900244 }
245 });
246
Daniel Park5a6a7102018-09-06 23:58:33 +0900247 if (osNode.vlanIntf() != null &&
248 !isIntfEnabled(osNode, osNode.vlanIntf())) {
249 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
250 osNode.vlanIntf(), deviceService, true);
251 }
daniel parkb18424c2018-02-05 15:43:43 +0900252 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900253 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900254 }
255 }
256
257 @Override
258 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900259 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900260 }
261
262 @Override
263 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900264 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900265 }
266
Daniel Parke2658ba2018-08-24 22:33:29 +0900267 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
268 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
269 return osNode.dpdkConfig().dpdkIntfs().stream()
270 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
271 }
272 return false;
273 }
274
275 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
276
277 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
278 if (client == null) {
279 log.info("Failed to get ovsdb client");
280 return false;
281 }
282
283 return client.getBridges().stream()
284 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
285 }
286
Jian Li340165f2018-02-27 10:38:17 +0900287 /**
Jian Li340165f2018-02-27 10:38:17 +0900288 * Creates a bridge with a given name on a given openstack node.
289 *
290 * @param osNode openstack node
291 * @param bridgeName bridge name
292 * @param deviceId device identifier
293 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900294 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
295 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900296
Jian Li789fadb2018-07-10 13:59:47 +0900297 List<ControllerInfo> controllers;
298
299 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
300 controllers = (List<ControllerInfo>) osNode.controllers();
301 } else {
302 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900303 .map(ControllerNode::ip)
304 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900305
Jian Li789fadb2018-07-10 13:59:47 +0900306 controllers = controllerIps.stream()
307 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
308 .collect(Collectors.toList());
309 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900310
311 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900312
Daniel Park92abf312018-08-08 17:01:35 +0900313 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900314 .name(bridgeName)
315 .failMode(BridgeDescription.FailMode.SECURE)
316 .datapathId(dpid)
317 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900318 .controllers(controllers);
319
320 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900321 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900322 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900323
324 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900325 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900326 }
327
Daniel Parke2658ba2018-08-24 22:33:29 +0900328 private void createDpdkTunnelBridge(OpenstackNode osNode) {
329 Device device = deviceService.getDevice(osNode.ovsdb());
330
331 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
332 .name(TUNNEL_BRIDGE)
333 .datapathType(NETDEV.name().toLowerCase());
334
335 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
336 bridgeConfig.addBridge(builder.build());
337 }
338
Jian Li340165f2018-02-27 10:38:17 +0900339 /**
Jian Li340165f2018-02-27 10:38:17 +0900340 * Creates a tunnel interface in a given openstack node.
341 *
342 * @param osNode openstack node
343 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900344 private void createTunnelInterface(OpenstackNode osNode) {
345 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
346 return;
347 }
348
349 Device device = deviceService.getDevice(osNode.ovsdb());
350 if (device == null || !device.is(InterfaceConfig.class)) {
351 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
352 return;
353 }
354
355 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
356 .deviceId(INTEGRATION_BRIDGE)
357 .ifaceName(DEFAULT_TUNNEL)
358 .type(TunnelDescription.Type.VXLAN)
359 .remote(TunnelEndPoints.flowTunnelEndpoint())
360 .key(TunnelKeys.flowTunnelKey())
361 .build();
362
363 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
364 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
365 }
366
Jian Li340165f2018-02-27 10:38:17 +0900367 /**
368 * Checks whether a given network interface in a given openstack node is enabled or not.
369 *
370 * @param osNode openstack node
371 * @param intf network interface name
372 * @return true if the given interface is enabled, false otherwise
373 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900374 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900375 return deviceService.isAvailable(osNode.intgBridge()) &&
376 deviceService.getPorts(osNode.intgBridge()).stream()
377 .anyMatch(port -> Objects.equals(
378 port.annotations().value(PORT_NAME), intf) &&
379 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900380 }
381
Jian Li340165f2018-02-27 10:38:17 +0900382 /**
383 * Checks whether all requirements for this state are fulfilled or not.
384 *
385 * @param osNode openstack node
386 * @return true if all requirements are fulfilled, false otherwise
387 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900388 private boolean isCurrentStateDone(OpenstackNode osNode) {
389 switch (osNode.state()) {
390 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900391 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
392 return false;
393 }
394
Daniel Parke2658ba2018-08-24 22:33:29 +0900395 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
396 if (hasDpdkTunnelBridge(osNode)) {
397 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
398 }
399 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900400 case DEVICE_CREATED:
401 if (osNode.dataIp() != null &&
402 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
403 return false;
404 }
405 if (osNode.vlanIntf() != null &&
406 !isIntfEnabled(osNode, osNode.vlanIntf())) {
407 return false;
408 }
daniel parkb18424c2018-02-05 15:43:43 +0900409 if (osNode.type() == GATEWAY &&
410 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900411 return false;
412 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900413 if (osNode.dpdkConfig() != null &&
414 osNode.dpdkConfig().dpdkIntfs() != null &&
415 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
416 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900417 }
Jian Lie6312162018-03-21 21:41:00 +0900418
419 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
420 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
421 return false;
422 }
423 }
424
Hyunsun Moon0d457362017-06-27 17:19:41 +0900425 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900426 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900427 case INCOMPLETE:
428 // always return false
429 // run init CLI to re-trigger node bootstrap
430 return false;
431 default:
432 return true;
433 }
434 }
435
Daniel Parke2658ba2018-08-24 22:33:29 +0900436 private boolean isDpdkIntfsCreated(OpenstackNode osNode, Collection<DpdkInterface> dpdkInterfaces) {
437 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
438 if (client == null) {
439 log.info("Failed to get ovsdb client");
440 return false;
441 }
442
443 Set<OvsdbPort> ports = client.getPorts();
444
445 for (DpdkInterface dpdkInterface : dpdkInterfaces) {
446 Optional<OvsdbPort> port = ports.stream()
447 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkInterface.intf()))
448 .findAny();
449
450 if (!port.isPresent()) {
451 return false;
452 }
453 Interface intf = client.getInterface(dpdkInterface.intf());
454 if (intf == null) {
455 return false;
456 }
457
458 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
459 if (mtu == null) {
460 return false;
461 }
462
463 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
464 if (option == null) {
465 return false;
466 }
467
468 if (!mtu.set().contains(dpdkInterface.mtu().intValue()) ||
469 !option.toString().contains(dpdkInterface.pciAddress())) {
470 log.trace("The dpdk interface {} was created but mtu or pci address is different from the config.");
471 return false;
472 }
473 }
474 return true;
475 }
476
Jian Li340165f2018-02-27 10:38:17 +0900477 /**
478 * Configures the openstack node with new state.
479 *
480 * @param osNode openstack node
481 * @param newState a new state
482 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900483 private void setState(OpenstackNode osNode, NodeState newState) {
484 if (osNode.state() == newState) {
485 return;
486 }
487 OpenstackNode updated = osNode.updateState(newState);
488 osNodeAdminService.updateNode(updated);
489 log.info("Changed {} state: {}", osNode.hostname(), newState);
490 }
491
Jian Li340165f2018-02-27 10:38:17 +0900492 /**
493 * Bootstraps a new openstack node.
494 *
495 * @param osNode openstack node
496 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900497 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900498 if (osNode.type() == CONTROLLER) {
499 if (osNode.state() == INIT && checkEndpoint(osNode)) {
500 setState(osNode, COMPLETE);
501 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900502 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900503 if (isCurrentStateDone(osNode)) {
504 setState(osNode, osNode.state().nextState());
505 } else {
Jian Li97482c12018-07-03 01:08:23 +0900506 log.trace("Processing {} state for {}", osNode.state(),
507 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900508 osNode.state().process(this, osNode);
509 }
510 }
511 }
512
Daniel Park5a6a7102018-09-06 23:58:33 +0900513 private void removeVlanInterface(OpenstackNode osNode) {
514 if (osNode.vlanIntf() != null) {
515 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
516
517 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkInterface);
518 }
519 }
520
521 private void removePhysicalInterface(OpenstackNode osNode) {
522 osNode.phyIntfs().forEach(phyIntf -> {
523 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
524
525 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkInterface);
526 });
527 }
528
529 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode, String intf) {
530 return osNode.dpdkConfig() == null ? Optional.empty() :
531 osNode.dpdkConfig().dpdkIntfs().stream()
532 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
533 .findAny();
534 }
535
536 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
537 String intfName,
538 Optional<DpdkInterface> dpdkInterface) {
539 if (dpdkInterface.isPresent()) {
540 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPort,
541 ovsdbController, false);
542 } else {
543 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
544 false);
545 }
546 }
547
548 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
549 //delete physical interfaces from the node
550 removePhysicalInterface(osNode);
551
552 //delete vlan interface from the node
553 removeVlanInterface(osNode);
554
555 //delete dpdk interfaces from the node
556 if (osNode.dpdkConfig() != null) {
557 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
558 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
559 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPort, ovsdbController, false);
560 }
561 });
562 }
563 }
564
Jian Li51b844c2018-05-31 10:59:03 +0900565 /**
566 * Checks the validity of the given endpoint.
567 *
568 * @param osNode gateway node
569 * @return validity result
570 */
571 private boolean checkEndpoint(OpenstackNode osNode) {
572 if (osNode == null) {
573 log.warn("Keystone auth info has not been configured. " +
574 "Please specify auth info via network-cfg.json.");
575 return false;
576 }
577
578 OSClient client = getConnectedClient(osNode);
579
580 if (client == null) {
581 return false;
582 } else {
583 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900584 }
585 }
586
Jian Li340165f2018-02-27 10:38:17 +0900587 /**
Jian Li97482c12018-07-03 01:08:23 +0900588 * Extracts properties from the component configuration context.
589 *
590 * @param context the component context
591 */
592 private void readComponentConfiguration(ComponentContext context) {
593 Dictionary<?, ?> properties = context.getProperties();
594
595 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
596 if (ovsdbPortConfigured == null) {
597 ovsdbPort = DEFAULT_OVSDB_PORT;
598 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
599 } else {
600 ovsdbPort = ovsdbPortConfigured;
601 log.info("Configured. OVSDB port is {}", ovsdbPort);
602 }
603
604 Boolean autoRecoveryConfigured =
605 getBooleanProperty(properties, AUTO_RECOVERY);
606 if (autoRecoveryConfigured == null) {
607 autoRecovery = DEFAULT_AUTO_RECOVERY;
608 log.info("Auto recovery flag is NOT " +
609 "configured, default value is {}", autoRecovery);
610 } else {
611 autoRecovery = autoRecoveryConfigured;
612 log.info("Configured. Auto recovery flag is {}", autoRecovery);
613 }
614 }
615
616 /**
Jian Li340165f2018-02-27 10:38:17 +0900617 * An internal OVSDB listener. This listener is used for listening the
618 * network facing events from OVSDB device. If a new OVSDB device is detected,
619 * ONOS tries to bootstrap the openstack node.
620 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900621 private class InternalOvsdbListener implements DeviceListener {
622
623 @Override
624 public boolean isRelevant(DeviceEvent event) {
625 NodeId leader = leadershipService.getLeader(appId.name());
626 return Objects.equals(localNode, leader) &&
627 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900628 osNodeService.node(event.subject().id()) != null &&
629 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900630 }
631
632 @Override
633 public void event(DeviceEvent event) {
634 Device device = event.subject();
635 OpenstackNode osNode = osNodeService.node(device.id());
636
637 switch (event.type()) {
638 case DEVICE_AVAILABILITY_CHANGED:
639 case DEVICE_ADDED:
640 eventExecutor.execute(() -> {
641 if (deviceService.isAvailable(device.id())) {
642 log.debug("OVSDB {} detected", device.id());
643 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900644 }
645 });
646 break;
647 case PORT_ADDED:
648 case PORT_REMOVED:
649 case DEVICE_REMOVED:
650 default:
651 // do nothing
652 break;
653 }
654 }
655 }
656
Jian Li340165f2018-02-27 10:38:17 +0900657 /**
658 * An internal integration bridge listener. This listener is used for
659 * listening the events from integration bridge. To listen the events from
660 * other types of bridge such as provider bridge or tunnel bridge, we need
661 * to augment OpenstackNodeService.node() method.
662 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900663 private class InternalBridgeListener implements DeviceListener {
664
665 @Override
666 public boolean isRelevant(DeviceEvent event) {
667 NodeId leader = leadershipService.getLeader(appId.name());
668 return Objects.equals(localNode, leader) &&
669 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900670 osNodeService.node(event.subject().id()) != null &&
671 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900672 }
673
674 @Override
675 public void event(DeviceEvent event) {
676 Device device = event.subject();
677 OpenstackNode osNode = osNodeService.node(device.id());
678
679 switch (event.type()) {
680 case DEVICE_AVAILABILITY_CHANGED:
681 case DEVICE_ADDED:
682 eventExecutor.execute(() -> {
683 if (deviceService.isAvailable(device.id())) {
684 log.debug("Integration bridge created on {}", osNode.hostname());
685 bootstrapNode(osNode);
686 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900687 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900688 setState(osNode, INCOMPLETE);
689 }
Jian Li97482c12018-07-03 01:08:23 +0900690
691 if (autoRecovery) {
692 if (osNode.state() == INCOMPLETE ||
693 osNode.state() == DEVICE_CREATED) {
694 log.info("Device {} is reconnected", device.id());
695 osNodeAdminService.updateNode(
696 osNode.updateState(NodeState.INIT));
697 }
698 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900699 });
700 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900701 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900702 case PORT_ADDED:
703 eventExecutor.execute(() -> {
704 Port port = event.port();
705 String portName = port.annotations().value(PORT_NAME);
706 if (osNode.state() == DEVICE_CREATED && (
707 Objects.equals(portName, DEFAULT_TUNNEL) ||
708 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900709 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900710 containsPhyIntf(osNode, portName)) ||
711 containsDpdkIntfs(osNode, portName)) {
712 log.info("Interface {} added or updated to {}",
713 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900714 bootstrapNode(osNode);
715 }
716 });
717 break;
718 case PORT_REMOVED:
719 eventExecutor.execute(() -> {
720 Port port = event.port();
721 String portName = port.annotations().value(PORT_NAME);
722 if (osNode.state() == COMPLETE && (
723 Objects.equals(portName, DEFAULT_TUNNEL) ||
724 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900725 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900726 containsPhyIntf(osNode, portName)) ||
727 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900728 log.warn("Interface {} removed from {}",
729 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900730 setState(osNode, INCOMPLETE);
731 }
732 });
733 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900734 case DEVICE_REMOVED:
735 default:
736 // do nothing
737 break;
738 }
739 }
740 }
741
Jian Li340165f2018-02-27 10:38:17 +0900742 /**
Jian Lie6312162018-03-21 21:41:00 +0900743 * Checks whether the openstack node contains the given physical interface.
744 *
745 * @param osNode openstack node
746 * @param portName physical interface
747 * @return true if openstack node contains the given physical interface,
748 * false otherwise
749 */
750 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900751 return osNode.phyIntfs().stream()
752 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
753 }
Jian Lie6312162018-03-21 21:41:00 +0900754
Daniel Park5a6a7102018-09-06 23:58:33 +0900755 /**
756 * Checks whether the openstack node contains the given dpdk interface.
757 *
758 * @param osNode openstack node
759 * @param portName dpdk interface
760 * @return true if openstack node contains the given dpdk interface,
761 * false otherwise
762 */
763 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
764 if (osNode.dpdkConfig() == null) {
765 return false;
766 }
767 return osNode.dpdkConfig().dpdkIntfs().stream()
768 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900769 }
770
771 /**
Jian Li340165f2018-02-27 10:38:17 +0900772 * An internal openstack node listener.
773 * The notification is triggered by OpenstackNodeStore.
774 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900775 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
776
777 @Override
778 public boolean isRelevant(OpenstackNodeEvent event) {
779 NodeId leader = leadershipService.getLeader(appId.name());
780 return Objects.equals(localNode, leader);
781 }
782
783 @Override
784 public void event(OpenstackNodeEvent event) {
785 switch (event.type()) {
786 case OPENSTACK_NODE_CREATED:
787 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900788 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900789 break;
790 case OPENSTACK_NODE_COMPLETE:
791 break;
792 case OPENSTACK_NODE_REMOVED:
Daniel Park5a6a7102018-09-06 23:58:33 +0900793 eventExecutor.execute(() -> processOpenstackNodeRemoved(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900794 break;
795 default:
796 break;
797 }
798 }
799 }
800}