blob: 924cb7cf5b1443e3971538da450f9d8b9a0a82c5 [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) {
550 //delete physical interfaces from the node
551 removePhysicalInterface(osNode);
552
553 //delete vlan interface from the node
554 removeVlanInterface(osNode);
555
556 //delete dpdk interfaces from the node
557 if (osNode.dpdkConfig() != null) {
558 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface -> {
559 if (isDpdkIntfsCreated(osNode, Lists.newArrayList(dpdkInterface))) {
560 addOrRemoveDpdkInterface(osNode, dpdkInterface, ovsdbPort, ovsdbController, false);
561 }
562 });
563 }
564 }
565
Jian Li51b844c2018-05-31 10:59:03 +0900566 /**
567 * Checks the validity of the given endpoint.
568 *
569 * @param osNode gateway node
570 * @return validity result
571 */
572 private boolean checkEndpoint(OpenstackNode osNode) {
573 if (osNode == null) {
574 log.warn("Keystone auth info has not been configured. " +
575 "Please specify auth info via network-cfg.json.");
576 return false;
577 }
578
579 OSClient client = getConnectedClient(osNode);
580
581 if (client == null) {
582 return false;
583 } else {
584 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900585 }
586 }
587
Jian Li340165f2018-02-27 10:38:17 +0900588 /**
Jian Li97482c12018-07-03 01:08:23 +0900589 * Extracts properties from the component configuration context.
590 *
591 * @param context the component context
592 */
593 private void readComponentConfiguration(ComponentContext context) {
594 Dictionary<?, ?> properties = context.getProperties();
595
596 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
597 if (ovsdbPortConfigured == null) {
598 ovsdbPort = DEFAULT_OVSDB_PORT;
599 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
600 } else {
601 ovsdbPort = ovsdbPortConfigured;
602 log.info("Configured. OVSDB port is {}", ovsdbPort);
603 }
604
605 Boolean autoRecoveryConfigured =
606 getBooleanProperty(properties, AUTO_RECOVERY);
607 if (autoRecoveryConfigured == null) {
608 autoRecovery = DEFAULT_AUTO_RECOVERY;
609 log.info("Auto recovery flag is NOT " +
610 "configured, default value is {}", autoRecovery);
611 } else {
612 autoRecovery = autoRecoveryConfigured;
613 log.info("Configured. Auto recovery flag is {}", autoRecovery);
614 }
615 }
616
617 /**
Jian Li340165f2018-02-27 10:38:17 +0900618 * An internal OVSDB listener. This listener is used for listening the
619 * network facing events from OVSDB device. If a new OVSDB device is detected,
620 * ONOS tries to bootstrap the openstack node.
621 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900622 private class InternalOvsdbListener implements DeviceListener {
623
624 @Override
625 public boolean isRelevant(DeviceEvent event) {
626 NodeId leader = leadershipService.getLeader(appId.name());
627 return Objects.equals(localNode, leader) &&
628 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900629 osNodeService.node(event.subject().id()) != null &&
630 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900631 }
632
633 @Override
634 public void event(DeviceEvent event) {
635 Device device = event.subject();
636 OpenstackNode osNode = osNodeService.node(device.id());
637
638 switch (event.type()) {
639 case DEVICE_AVAILABILITY_CHANGED:
640 case DEVICE_ADDED:
641 eventExecutor.execute(() -> {
642 if (deviceService.isAvailable(device.id())) {
643 log.debug("OVSDB {} detected", device.id());
644 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900645 }
646 });
647 break;
648 case PORT_ADDED:
649 case PORT_REMOVED:
650 case DEVICE_REMOVED:
651 default:
652 // do nothing
653 break;
654 }
655 }
656 }
657
Jian Li340165f2018-02-27 10:38:17 +0900658 /**
659 * An internal integration bridge listener. This listener is used for
660 * listening the events from integration bridge. To listen the events from
661 * other types of bridge such as provider bridge or tunnel bridge, we need
662 * to augment OpenstackNodeService.node() method.
663 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900664 private class InternalBridgeListener implements DeviceListener {
665
666 @Override
667 public boolean isRelevant(DeviceEvent event) {
668 NodeId leader = leadershipService.getLeader(appId.name());
669 return Objects.equals(localNode, leader) &&
670 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900671 osNodeService.node(event.subject().id()) != null &&
672 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900673 }
674
675 @Override
676 public void event(DeviceEvent event) {
677 Device device = event.subject();
678 OpenstackNode osNode = osNodeService.node(device.id());
679
680 switch (event.type()) {
681 case DEVICE_AVAILABILITY_CHANGED:
682 case DEVICE_ADDED:
683 eventExecutor.execute(() -> {
684 if (deviceService.isAvailable(device.id())) {
685 log.debug("Integration bridge created on {}", osNode.hostname());
686 bootstrapNode(osNode);
687 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900688 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900689 setState(osNode, INCOMPLETE);
690 }
Jian Li97482c12018-07-03 01:08:23 +0900691
692 if (autoRecovery) {
693 if (osNode.state() == INCOMPLETE ||
694 osNode.state() == DEVICE_CREATED) {
695 log.info("Device {} is reconnected", device.id());
696 osNodeAdminService.updateNode(
697 osNode.updateState(NodeState.INIT));
698 }
699 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900700 });
701 break;
Daniel Park5a6a7102018-09-06 23:58:33 +0900702 case PORT_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900703 case PORT_ADDED:
704 eventExecutor.execute(() -> {
705 Port port = event.port();
706 String portName = port.annotations().value(PORT_NAME);
707 if (osNode.state() == DEVICE_CREATED && (
708 Objects.equals(portName, DEFAULT_TUNNEL) ||
709 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900710 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900711 containsPhyIntf(osNode, portName)) ||
712 containsDpdkIntfs(osNode, portName)) {
713 log.info("Interface {} added or updated to {}",
714 portName, device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900715 bootstrapNode(osNode);
716 }
717 });
718 break;
719 case PORT_REMOVED:
720 eventExecutor.execute(() -> {
721 Port port = event.port();
722 String portName = port.annotations().value(PORT_NAME);
723 if (osNode.state() == COMPLETE && (
724 Objects.equals(portName, DEFAULT_TUNNEL) ||
725 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900726 Objects.equals(portName, osNode.uplinkPort()) ||
Daniel Park5a6a7102018-09-06 23:58:33 +0900727 containsPhyIntf(osNode, portName)) ||
728 containsDpdkIntfs(osNode, portName)) {
Jian Li97482c12018-07-03 01:08:23 +0900729 log.warn("Interface {} removed from {}",
730 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900731 setState(osNode, INCOMPLETE);
732 }
733 });
734 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900735 case DEVICE_REMOVED:
736 default:
737 // do nothing
738 break;
739 }
740 }
741 }
742
Jian Li340165f2018-02-27 10:38:17 +0900743 /**
Jian Lie6312162018-03-21 21:41:00 +0900744 * Checks whether the openstack node contains the given physical interface.
745 *
746 * @param osNode openstack node
747 * @param portName physical interface
748 * @return true if openstack node contains the given physical interface,
749 * false otherwise
750 */
751 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
Daniel Park5a6a7102018-09-06 23:58:33 +0900752 return osNode.phyIntfs().stream()
753 .anyMatch(phyInterface -> phyInterface.intf().equals(portName));
754 }
Jian Lie6312162018-03-21 21:41:00 +0900755
Daniel Park5a6a7102018-09-06 23:58:33 +0900756 /**
757 * Checks whether the openstack node contains the given dpdk interface.
758 *
759 * @param osNode openstack node
760 * @param portName dpdk interface
761 * @return true if openstack node contains the given dpdk interface,
762 * false otherwise
763 */
764 private boolean containsDpdkIntfs(OpenstackNode osNode, String portName) {
765 if (osNode.dpdkConfig() == null) {
766 return false;
767 }
768 return osNode.dpdkConfig().dpdkIntfs().stream()
769 .anyMatch(dpdkInterface -> dpdkInterface.intf().equals(portName));
Jian Lie6312162018-03-21 21:41:00 +0900770 }
771
772 /**
Jian Li340165f2018-02-27 10:38:17 +0900773 * An internal openstack node listener.
774 * The notification is triggered by OpenstackNodeStore.
775 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900776 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
777
778 @Override
779 public boolean isRelevant(OpenstackNodeEvent event) {
780 NodeId leader = leadershipService.getLeader(appId.name());
781 return Objects.equals(localNode, leader);
782 }
783
784 @Override
785 public void event(OpenstackNodeEvent event) {
786 switch (event.type()) {
787 case OPENSTACK_NODE_CREATED:
788 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900789 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900790 break;
791 case OPENSTACK_NODE_COMPLETE:
792 break;
793 case OPENSTACK_NODE_REMOVED:
Daniel Park5a6a7102018-09-06 23:58:33 +0900794 eventExecutor.execute(() -> processOpenstackNodeRemoved(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900795 break;
796 default:
797 break;
798 }
799 }
800 }
801}