blob: 98ee29e7b2a7342bca36fb60fadcfbde59bc0a4d [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
Daniel Park5a6a7102018-09-06 23:58:33 +090018import com.google.common.collect.Lists;
Hyunsun Moon0d457362017-06-27 17:19:41 +090019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.IpAddress;
27import org.onlab.util.Tools;
28import org.onosproject.cfg.ComponentConfigService;
29import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.ControllerNode;
31import org.onosproject.cluster.LeadershipService;
32import org.onosproject.cluster.NodeId;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090035import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Port;
38import org.onosproject.net.behaviour.BridgeConfig;
39import org.onosproject.net.behaviour.BridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090040import org.onosproject.net.behaviour.ControllerInfo;
41import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090042import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090043import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon0d457362017-06-27 17:19:41 +090044import org.onosproject.net.behaviour.TunnelDescription;
45import org.onosproject.net.behaviour.TunnelEndPoints;
46import org.onosproject.net.behaviour.TunnelKeys;
47import org.onosproject.net.device.DeviceAdminService;
48import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
Daniel Parke2658ba2018-08-24 22:33:29 +090051import org.onosproject.openstacknode.api.DpdkInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090052import org.onosproject.openstacknode.api.NodeState;
53import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090054import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
55import org.onosproject.openstacknode.api.OpenstackNodeEvent;
56import org.onosproject.openstacknode.api.OpenstackNodeHandler;
57import org.onosproject.openstacknode.api.OpenstackNodeListener;
58import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090059import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Daniel Parke2658ba2018-08-24 22:33:29 +090060import org.onosproject.ovsdb.controller.OvsdbClientService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090061import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Parke2658ba2018-08-24 22:33:29 +090062import org.onosproject.ovsdb.controller.OvsdbPort;
63import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
64import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
65import org.onosproject.ovsdb.rfc.table.Interface;
Jian Li51b844c2018-05-31 10:59:03 +090066import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import org.osgi.service.component.ComponentContext;
68import org.slf4j.Logger;
69
Daniel Parke2658ba2018-08-24 22:33:29 +090070import java.util.Collection;
Hyunsun Moon0d457362017-06-27 17:19:41 +090071import java.util.Dictionary;
72import java.util.List;
73import java.util.Objects;
Daniel Parke2658ba2018-08-24 22:33:29 +090074import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090075import java.util.Set;
76import java.util.concurrent.ExecutorService;
77import java.util.stream.Collectors;
78
Hyunsun Moon0d457362017-06-27 17:19:41 +090079import static java.util.concurrent.Executors.newSingleThreadExecutor;
80import static org.onlab.packet.TpPort.tpPort;
81import static org.onlab.util.Tools.groupedThreads;
82import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li5afbea42018-02-28 10:37:03 +090083import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
84import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090085import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090086import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090087import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
88import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
89import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090090import static org.onosproject.openstacknode.api.NodeState.INIT;
91import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090092import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
93import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Daniel Park5a6a7102018-09-06 23:58:33 +090094import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveDpdkInterface;
95import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Li97482c12018-07-03 01:08:23 +090096import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090097import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +090098import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090099import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100import static org.slf4j.LoggerFactory.getLogger;
101
102/**
103 * Service bootstraps openstack node based on its type.
104 */
105@Component(immediate = true)
106public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
107
Jian Li5afbea42018-02-28 10:37:03 +0900108 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900109
110 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900111 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900112 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900113 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900114 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900115 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900116 private static final int DPID_BEGIN = 3;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected CoreService coreService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected LeadershipService leadershipService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected ClusterService clusterService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected DeviceService deviceService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected DeviceAdminService deviceAdminService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected OvsdbController ovsdbController;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900137 protected OpenstackNodeService osNodeService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected OpenstackNodeAdminService osNodeAdminService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected ComponentConfigService componentConfigService;
144
145 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
146 label = "OVSDB server listen port")
147 private int ovsdbPort = DEFAULT_OVSDB_PORT;
148
Jian Li97482c12018-07-03 01:08:23 +0900149 @Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
150 label = "A flag which indicates whether auto-recover openstack " +
151 "node status at the receiving of switch reconnecting event.")
152 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
153
Hyunsun Moon0d457362017-06-27 17:19:41 +0900154 private final ExecutorService eventExecutor = newSingleThreadExecutor(
155 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
156
157 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
158 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900159 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
160
161 private ApplicationId appId;
162 private NodeId localNode;
163
164 @Activate
165 protected void activate() {
166 appId = coreService.getAppId(APP_ID);
167 localNode = clusterService.getLocalNode().id();
168
169 componentConfigService.registerProperties(getClass());
170 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900171 deviceService.addListener(ovsdbListener);
172 deviceService.addListener(bridgeListener);
173 osNodeService.addListener(osNodeListener);
174
175 log.info("Started");
176 }
177
178 @Deactivate
179 protected void deactivate() {
180 osNodeService.removeListener(osNodeListener);
181 deviceService.removeListener(bridgeListener);
182 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900183 componentConfigService.unregisterProperties(getClass(), false);
184 leadershipService.withdraw(appId.name());
185 eventExecutor.shutdown();
186
187 log.info("Stopped");
188 }
189
190 @Modified
191 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900192 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900193
194 log.info("Modified");
195 }
196
197 @Override
198 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900199 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900200 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
201 return;
202 }
203 if (!deviceService.isAvailable(osNode.intgBridge())) {
204 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
205 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900206 if (hasDpdkTunnelBridge(osNode)) {
207 createDpdkTunnelBridge(osNode);
208 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900209 }
210
211 @Override
212 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900213 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900214 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900215 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
216 return;
217 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900218
daniel parkb18424c2018-02-05 15:43:43 +0900219 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900220 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900221 osNode.uplinkPort(), deviceService, true);
daniel parkb18424c2018-02-05 15:43:43 +0900222 }
223
224 if (osNode.dataIp() != null &&
225 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
226 createTunnelInterface(osNode);
227 }
228
Daniel Parke2658ba2018-08-24 22:33:29 +0900229 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900230 osNode.dpdkConfig().dpdkIntfs().stream()
231 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(TUNNEL_BRIDGE))
232 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
233 osNode, dpdkInterface, ovsdbPort, ovsdbController, true));
234
235 osNode.dpdkConfig().dpdkIntfs().stream()
236 .filter(dpdkInterface -> dpdkInterface.deviceName().equals(INTEGRATION_BRIDGE))
237 .forEach(dpdkInterface -> addOrRemoveDpdkInterface(
238 osNode, dpdkInterface, ovsdbPort, ovsdbController, true));
Daniel Parke2658ba2018-08-24 22:33:29 +0900239 }
240
Jian Lie6312162018-03-21 21:41:00 +0900241 osNode.phyIntfs().forEach(i -> {
242 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900243 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
Daniel Park5a6a7102018-09-06 23:58:33 +0900244 i.intf(), deviceService, true);
Jian Lie6312162018-03-21 21:41:00 +0900245 }
246 });
247
Daniel Park5a6a7102018-09-06 23:58:33 +0900248 if (osNode.vlanIntf() != null &&
249 !isIntfEnabled(osNode, osNode.vlanIntf())) {
250 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
251 osNode.vlanIntf(), deviceService, true);
252 }
daniel parkb18424c2018-02-05 15:43:43 +0900253 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900254 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900255 }
256 }
257
258 @Override
259 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900260 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900261 }
262
263 @Override
264 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900265 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900266 }
267
Daniel Parke2658ba2018-08-24 22:33:29 +0900268 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
269 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
270 return osNode.dpdkConfig().dpdkIntfs().stream()
271 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
272 }
273 return false;
274 }
275
276 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
277
278 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
279 if (client == null) {
280 log.info("Failed to get ovsdb client");
281 return false;
282 }
283
284 return client.getBridges().stream()
285 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
286 }
287
Jian Li340165f2018-02-27 10:38:17 +0900288 /**
Jian Li340165f2018-02-27 10:38:17 +0900289 * Creates a bridge with a given name on a given openstack node.
290 *
291 * @param osNode openstack node
292 * @param bridgeName bridge name
293 * @param deviceId device identifier
294 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900295 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
296 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900297
Jian Li789fadb2018-07-10 13:59:47 +0900298 List<ControllerInfo> controllers;
299
300 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
301 controllers = (List<ControllerInfo>) osNode.controllers();
302 } else {
303 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900304 .map(ControllerNode::ip)
305 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306
Jian Li789fadb2018-07-10 13:59:47 +0900307 controllers = controllerIps.stream()
308 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
309 .collect(Collectors.toList());
310 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900311
312 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900313
Daniel Park92abf312018-08-08 17:01:35 +0900314 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900315 .name(bridgeName)
316 .failMode(BridgeDescription.FailMode.SECURE)
317 .datapathId(dpid)
318 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900319 .controllers(controllers);
320
321 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900322 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900323 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900324
325 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900326 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900327 }
328
Daniel Parke2658ba2018-08-24 22:33:29 +0900329 private void createDpdkTunnelBridge(OpenstackNode osNode) {
330 Device device = deviceService.getDevice(osNode.ovsdb());
331
332 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
333 .name(TUNNEL_BRIDGE)
334 .datapathType(NETDEV.name().toLowerCase());
335
336 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
337 bridgeConfig.addBridge(builder.build());
338 }
339
Jian Li340165f2018-02-27 10:38:17 +0900340 /**
Jian Li340165f2018-02-27 10:38:17 +0900341 * Creates a tunnel interface in a given openstack node.
342 *
343 * @param osNode openstack node
344 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900345 private void createTunnelInterface(OpenstackNode osNode) {
346 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
347 return;
348 }
349
350 Device device = deviceService.getDevice(osNode.ovsdb());
351 if (device == null || !device.is(InterfaceConfig.class)) {
352 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
353 return;
354 }
355
356 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
357 .deviceId(INTEGRATION_BRIDGE)
358 .ifaceName(DEFAULT_TUNNEL)
359 .type(TunnelDescription.Type.VXLAN)
360 .remote(TunnelEndPoints.flowTunnelEndpoint())
361 .key(TunnelKeys.flowTunnelKey())
362 .build();
363
364 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
365 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
366 }
367
Jian Li340165f2018-02-27 10:38:17 +0900368 /**
369 * Checks whether a given network interface in a given openstack node is enabled or not.
370 *
371 * @param osNode openstack node
372 * @param intf network interface name
373 * @return true if the given interface is enabled, false otherwise
374 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900375 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900376 return deviceService.isAvailable(osNode.intgBridge()) &&
377 deviceService.getPorts(osNode.intgBridge()).stream()
378 .anyMatch(port -> Objects.equals(
379 port.annotations().value(PORT_NAME), intf) &&
380 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900381 }
382
Jian Li340165f2018-02-27 10:38:17 +0900383 /**
384 * Checks whether all requirements for this state are fulfilled or not.
385 *
386 * @param osNode openstack node
387 * @return true if all requirements are fulfilled, false otherwise
388 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900389 private boolean isCurrentStateDone(OpenstackNode osNode) {
390 switch (osNode.state()) {
391 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900392 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
393 return false;
394 }
395
Daniel Parke2658ba2018-08-24 22:33:29 +0900396 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
397 if (hasDpdkTunnelBridge(osNode)) {
398 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
399 }
400 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900401 case DEVICE_CREATED:
402 if (osNode.dataIp() != null &&
403 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
404 return false;
405 }
406 if (osNode.vlanIntf() != null &&
407 !isIntfEnabled(osNode, osNode.vlanIntf())) {
408 return false;
409 }
daniel parkb18424c2018-02-05 15:43:43 +0900410 if (osNode.type() == GATEWAY &&
411 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900412 return false;
413 }
Daniel Park5a6a7102018-09-06 23:58:33 +0900414 if (osNode.dpdkConfig() != null &&
415 osNode.dpdkConfig().dpdkIntfs() != null &&
416 !isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs())) {
417 return false;
Daniel Parke2658ba2018-08-24 22:33:29 +0900418 }
Jian Lie6312162018-03-21 21:41:00 +0900419
420 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
421 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
422 return false;
423 }
424 }
425
Hyunsun Moon0d457362017-06-27 17:19:41 +0900426 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900427 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900428 case INCOMPLETE:
429 // always return false
430 // run init CLI to re-trigger node bootstrap
431 return false;
432 default:
433 return true;
434 }
435 }
436
Daniel Parke2658ba2018-08-24 22:33:29 +0900437 private boolean isDpdkIntfsCreated(OpenstackNode osNode, Collection<DpdkInterface> dpdkInterfaces) {
438 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
439 if (client == null) {
440 log.info("Failed to get ovsdb client");
441 return false;
442 }
443
444 Set<OvsdbPort> ports = client.getPorts();
445
446 for (DpdkInterface dpdkInterface : dpdkInterfaces) {
447 Optional<OvsdbPort> port = ports.stream()
448 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkInterface.intf()))
449 .findAny();
450
451 if (!port.isPresent()) {
452 return false;
453 }
454 Interface intf = client.getInterface(dpdkInterface.intf());
455 if (intf == null) {
456 return false;
457 }
458
459 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
460 if (mtu == null) {
461 return false;
462 }
463
464 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
465 if (option == null) {
466 return false;
467 }
468
469 if (!mtu.set().contains(dpdkInterface.mtu().intValue()) ||
470 !option.toString().contains(dpdkInterface.pciAddress())) {
471 log.trace("The dpdk interface {} was created but mtu or pci address is different from the config.");
472 return false;
473 }
474 }
475 return true;
476 }
477
Jian Li340165f2018-02-27 10:38:17 +0900478 /**
479 * Configures the openstack node with new state.
480 *
481 * @param osNode openstack node
482 * @param newState a new state
483 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900484 private void setState(OpenstackNode osNode, NodeState newState) {
485 if (osNode.state() == newState) {
486 return;
487 }
488 OpenstackNode updated = osNode.updateState(newState);
489 osNodeAdminService.updateNode(updated);
490 log.info("Changed {} state: {}", osNode.hostname(), newState);
491 }
492
Jian Li340165f2018-02-27 10:38:17 +0900493 /**
494 * Bootstraps a new openstack node.
495 *
496 * @param osNode openstack node
497 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900498 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900499 if (osNode.type() == CONTROLLER) {
500 if (osNode.state() == INIT && checkEndpoint(osNode)) {
501 setState(osNode, COMPLETE);
502 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900503 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900504 if (isCurrentStateDone(osNode)) {
505 setState(osNode, osNode.state().nextState());
506 } else {
Jian Li97482c12018-07-03 01:08:23 +0900507 log.trace("Processing {} state for {}", osNode.state(),
508 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900509 osNode.state().process(this, osNode);
510 }
511 }
512 }
513
Daniel Park5a6a7102018-09-06 23:58:33 +0900514 private void removeVlanInterface(OpenstackNode osNode) {
515 if (osNode.vlanIntf() != null) {
516 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, osNode.vlanIntf());
517
518 removeInterfaceOnIntegrationBridge(osNode, osNode.vlanIntf(), dpdkInterface);
519 }
520 }
521
522 private void removePhysicalInterface(OpenstackNode osNode) {
523 osNode.phyIntfs().forEach(phyIntf -> {
524 Optional<DpdkInterface> dpdkInterface = dpdkInterfaceByIntfName(osNode, phyIntf.intf());
525
526 removeInterfaceOnIntegrationBridge(osNode, phyIntf.intf(), dpdkInterface);
527 });
528 }
529
530 private Optional<DpdkInterface> dpdkInterfaceByIntfName(OpenstackNode osNode, String intf) {
531 return osNode.dpdkConfig() == null ? Optional.empty() :
532 osNode.dpdkConfig().dpdkIntfs().stream()
533 .filter(dpdkIntf -> dpdkIntf.intf().equals(intf))
534 .findAny();
535 }
536
537 private void removeInterfaceOnIntegrationBridge(OpenstackNode osNode,
538 String intfName,
539 Optional<DpdkInterface> dpdkInterface) {
540 if (dpdkInterface.isPresent()) {
541 addOrRemoveDpdkInterface(osNode, dpdkInterface.get(), ovsdbPort,
542 ovsdbController, false);
543 } else {
544 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, intfName, deviceService,
545 false);
546 }
547 }
548
549 private void processOpenstackNodeRemoved(OpenstackNode osNode) {
Daniel Park489645c2018-10-24 11:34:22 +0900550 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
551 if (client == null) {
552 log.info("Failed to get ovsdb client");
553 return;
554 }
555
Daniel Park5a6a7102018-09-06 23:58:33 +0900556 //delete physical interfaces from the node
557 removePhysicalInterface(osNode);
558
559 //delete vlan interface from the node
560 removeVlanInterface(osNode);
561
562 //delete dpdk interfaces from the node
563 if (osNode.dpdkConfig() != null) {
564 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
565 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
566 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPort, ovsdbController, false);
567 }
568 });
569 }
Daniel Park489645c2018-10-24 11:34:22 +0900570
571 //delete tunnel bridge from the node
572 if (hasDpdkTunnelBridge(osNode)) {
573 client.dropBridge(TUNNEL_BRIDGE);
574 }
575
576 //delete integration bridge from the node
577 client.dropBridge(INTEGRATION_BRIDGE);
578
579 //disconnect ovsdb
580 client.disconnect();
Daniel Park5a6a7102018-09-06 23:58:33 +0900581 }
582
Jian Li51b844c2018-05-31 10:59:03 +0900583 /**
584 * Checks the validity of the given endpoint.
585 *
586 * @param osNode gateway node
587 * @return validity result
588 */
589 private boolean checkEndpoint(OpenstackNode osNode) {
590 if (osNode == null) {
591 log.warn("Keystone auth info has not been configured. " +
592 "Please specify auth info via network-cfg.json.");
593 return false;
594 }
595
596 OSClient client = getConnectedClient(osNode);
597
598 if (client == null) {
599 return false;
600 } else {
601 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900602 }
603 }
604
Jian Li340165f2018-02-27 10:38:17 +0900605 /**
Jian Li97482c12018-07-03 01:08:23 +0900606 * Extracts properties from the component configuration context.
607 *
608 * @param context the component context
609 */
610 private void readComponentConfiguration(ComponentContext context) {
611 Dictionary<?, ?> properties = context.getProperties();
612
613 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
614 if (ovsdbPortConfigured == null) {
615 ovsdbPort = DEFAULT_OVSDB_PORT;
616 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
617 } else {
618 ovsdbPort = ovsdbPortConfigured;
619 log.info("Configured. OVSDB port is {}", ovsdbPort);
620 }
621
622 Boolean autoRecoveryConfigured =
623 getBooleanProperty(properties, AUTO_RECOVERY);
624 if (autoRecoveryConfigured == null) {
625 autoRecovery = DEFAULT_AUTO_RECOVERY;
626 log.info("Auto recovery flag is NOT " +
627 "configured, default value is {}", autoRecovery);
628 } else {
629 autoRecovery = autoRecoveryConfigured;
630 log.info("Configured. Auto recovery flag is {}", autoRecovery);
631 }
632 }
633
634 /**
Jian Li340165f2018-02-27 10:38:17 +0900635 * An internal OVSDB listener. This listener is used for listening the
636 * network facing events from OVSDB device. If a new OVSDB device is detected,
637 * ONOS tries to bootstrap the openstack node.
638 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900639 private class InternalOvsdbListener implements DeviceListener {
640
641 @Override
642 public boolean isRelevant(DeviceEvent event) {
643 NodeId leader = leadershipService.getLeader(appId.name());
644 return Objects.equals(localNode, leader) &&
Jian Licab41762018-11-06 12:30:08 +0900645 event.subject().type() == Device.Type.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900646 }
647
648 @Override
649 public void event(DeviceEvent event) {
650 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900651
652 switch (event.type()) {
653 case DEVICE_AVAILABILITY_CHANGED:
654 case DEVICE_ADDED:
655 eventExecutor.execute(() -> {
Jian Licab41762018-11-06 12:30:08 +0900656 OpenstackNode osNode = osNodeService.node(device.id());
657
658 if (osNode == null || osNode.type() == CONTROLLER) {
659 return;
660 }
661
Hyunsun Moon0d457362017-06-27 17:19:41 +0900662 if (deviceService.isAvailable(device.id())) {
663 log.debug("OVSDB {} detected", device.id());
664 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900665 }
666 });
667 break;
668 case PORT_ADDED:
669 case PORT_REMOVED:
670 case DEVICE_REMOVED:
671 default:
672 // do nothing
673 break;
674 }
675 }
676 }
677
Jian Li340165f2018-02-27 10:38:17 +0900678 /**
679 * An internal integration bridge listener. This listener is used for
680 * listening the events from integration bridge. To listen the events from
681 * other types of bridge such as provider bridge or tunnel bridge, we need
682 * to augment OpenstackNodeService.node() method.
683 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900684 private class InternalBridgeListener implements DeviceListener {
685
686 @Override
687 public boolean isRelevant(DeviceEvent event) {
688 NodeId leader = leadershipService.getLeader(appId.name());
689 return Objects.equals(localNode, leader) &&
Jian Licab41762018-11-06 12:30:08 +0900690 event.subject().type() == Device.Type.SWITCH;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900691 }
692
693 @Override
694 public void event(DeviceEvent event) {
695 Device device = event.subject();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900696
697 switch (event.type()) {
698 case DEVICE_AVAILABILITY_CHANGED:
699 case DEVICE_ADDED:
700 eventExecutor.execute(() -> {
Jian Licab41762018-11-06 12:30:08 +0900701 OpenstackNode osNode = osNodeService.node(device.id());
702
703 if (osNode == null || osNode.type() == CONTROLLER) {
704 return;
705 }
706
Hyunsun Moon0d457362017-06-27 17:19:41 +0900707 if (deviceService.isAvailable(device.id())) {
708 log.debug("Integration bridge created on {}", osNode.hostname());
709 bootstrapNode(osNode);
710 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900711 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900712 setState(osNode, INCOMPLETE);
713 }
Jian Li97482c12018-07-03 01:08:23 +0900714
715 if (autoRecovery) {
716 if (osNode.state() == INCOMPLETE ||
717 osNode.state() == DEVICE_CREATED) {
718 log.info("Device {} is reconnected", device.id());
719 osNodeAdminService.updateNode(
720 osNode.updateState(NodeState.INIT));
721 }
722 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900723 });
724 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900725 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900726 case PORT_ADDED:
727 eventExecutor.execute(() -> {
Jian Licab41762018-11-06 12:30:08 +0900728 OpenstackNode osNode = osNodeService.node(device.id());
729
730 if (osNode == null || osNode.type() == CONTROLLER) {
731 return;
732 }
733
Hyunsun Moon0d457362017-06-27 17:19:41 +0900734 Port port = event.port();
735 String portName = port.annotations().value(PORT_NAME);
736 if (osNode.state() == DEVICE_CREATED && (
737 Objects.equals(portName, DEFAULT_TUNNEL) ||
738 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900739 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900740 containsPhyIntf(osNode, portName)) ||
741 containsDpdkIntfs(osNode, portName)) {
742 log.info("Interface {} added or updated to {}",
743 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900744 bootstrapNode(osNode);
745 }
746 });
747 break;
748 case PORT_REMOVED:
749 eventExecutor.execute(() -> {
Jian Licab41762018-11-06 12:30:08 +0900750 OpenstackNode osNode = osNodeService.node(device.id());
751
752 if (osNode == null || osNode.type() == CONTROLLER) {
753 return;
754 }
755
Hyunsun Moon0d457362017-06-27 17:19:41 +0900756 Port port = event.port();
757 String portName = port.annotations().value(PORT_NAME);
758 if (osNode.state() == COMPLETE && (
759 Objects.equals(portName, DEFAULT_TUNNEL) ||
760 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900761 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900762 containsPhyIntf(osNode, portName)) ||
763 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900764 log.warn("Interface {} removed from {}",
765 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900766 setState(osNode, INCOMPLETE);
767 }
768 });
769 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900770 case DEVICE_REMOVED:
771 default:
772 // do nothing
773 break;
774 }
775 }
776 }
777
Jian Li340165f2018-02-27 10:38:17 +0900778 /**
Jian Lie6312162018-03-21 21:41:00 +0900779 * Checks whether the openstack node contains the given physical interface.
780 *
781 * @param osNode openstack node
782 * @param portName physical interface
783 * @return true if openstack node contains the given physical interface,
784 * false otherwise
785 */
786 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900787 return osNode.phyIntfs().stream()
788 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
789 }
Jian Lie6312162018-03-21 21:41:00 +0900790
Daniel Park5a6a7102018-09-06 23:58:33 +0900791 /**
792 * Checks whether the openstack node contains the given dpdk interface.
793 *
794 * @param osNode openstack node
795 * @param portName dpdk interface
796 * @return true if openstack node contains the given dpdk interface,
797 * false otherwise
798 */
799 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
800 if (osNode.dpdkConfig() == null) {
801 return false;
802 }
803 return osNode.dpdkConfig().dpdkIntfs().stream()
804 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900805 }
806
807 /**
Jian Li340165f2018-02-27 10:38:17 +0900808 * An internal openstack node listener.
809 * The notification is triggered by OpenstackNodeStore.
810 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900811 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
812
813 @Override
814 public boolean isRelevant(OpenstackNodeEvent event) {
815 NodeId leader = leadershipService.getLeader(appId.name());
816 return Objects.equals(localNode, leader);
817 }
818
819 @Override
820 public void event(OpenstackNodeEvent event) {
821 switch (event.type()) {
822 case OPENSTACK_NODE_CREATED:
823 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900824 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900825 break;
826 case OPENSTACK_NODE_COMPLETE:
827 break;
828 case OPENSTACK_NODE_REMOVED:
Daniel Park5a6a7102018-09-06 23:58:33 +0900829 eventExecutor.execute(() -> processOpenstackNodeRemoved(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900830 break;
831 default:
832 break;
833 }
834 }
835 }
836}