blob: 0cf78fad33309a501d5fc8619cdb34e7db7a9e5c [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 Parke2658ba2018-08-24 22:33:29 +090018import com.google.common.collect.Maps;
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;
40import org.onosproject.net.behaviour.BridgeName;
41import org.onosproject.net.behaviour.ControllerInfo;
42import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090043import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090044import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon0d457362017-06-27 17:19:41 +090045import org.onosproject.net.behaviour.TunnelDescription;
46import org.onosproject.net.behaviour.TunnelEndPoints;
47import org.onosproject.net.behaviour.TunnelKeys;
48import org.onosproject.net.device.DeviceAdminService;
49import org.onosproject.net.device.DeviceEvent;
50import org.onosproject.net.device.DeviceListener;
51import org.onosproject.net.device.DeviceService;
Daniel Parke2658ba2018-08-24 22:33:29 +090052import org.onosproject.openstacknode.api.DpdkInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090053import org.onosproject.openstacknode.api.NodeState;
54import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090055import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
56import org.onosproject.openstacknode.api.OpenstackNodeEvent;
57import org.onosproject.openstacknode.api.OpenstackNodeHandler;
58import org.onosproject.openstacknode.api.OpenstackNodeListener;
59import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090060import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Daniel Parke2658ba2018-08-24 22:33:29 +090061import org.onosproject.ovsdb.controller.OvsdbClientService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090062import org.onosproject.ovsdb.controller.OvsdbController;
Daniel Parke2658ba2018-08-24 22:33:29 +090063import org.onosproject.ovsdb.controller.OvsdbInterface;
64import org.onosproject.ovsdb.controller.OvsdbPort;
65import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
66import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
67import org.onosproject.ovsdb.rfc.table.Interface;
Jian Li51b844c2018-05-31 10:59:03 +090068import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090069import org.osgi.service.component.ComponentContext;
70import org.slf4j.Logger;
71
Daniel Parke2658ba2018-08-24 22:33:29 +090072import java.util.Collection;
Hyunsun Moon0d457362017-06-27 17:19:41 +090073import java.util.Dictionary;
74import java.util.List;
Daniel Parke2658ba2018-08-24 22:33:29 +090075import java.util.Map;
Hyunsun Moon0d457362017-06-27 17:19:41 +090076import java.util.Objects;
Daniel Parke2658ba2018-08-24 22:33:29 +090077import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090078import java.util.Set;
79import java.util.concurrent.ExecutorService;
80import java.util.stream.Collectors;
81
Hyunsun Moon0d457362017-06-27 17:19:41 +090082import static java.util.concurrent.Executors.newSingleThreadExecutor;
83import static org.onlab.packet.TpPort.tpPort;
84import static org.onlab.util.Tools.groupedThreads;
85import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li5afbea42018-02-28 10:37:03 +090086import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
87import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Daniel Parke2658ba2018-08-24 22:33:29 +090088import static org.onosproject.openstacknode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090089import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090090import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
91import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
92import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090093import static org.onosproject.openstacknode.api.NodeState.INIT;
94import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090095import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
96import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Jian Li97482c12018-07-03 01:08:23 +090097import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090098import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parke2658ba2018-08-24 22:33:29 +090099import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getOvsdbClient;
Daniel Parkc4d06402018-05-28 15:57:37 +0900100import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900101import static org.slf4j.LoggerFactory.getLogger;
102
103/**
104 * Service bootstraps openstack node based on its type.
105 */
106@Component(immediate = true)
107public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
108
Jian Li5afbea42018-02-28 10:37:03 +0900109 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900110
111 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900112 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900113 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parke2658ba2018-08-24 22:33:29 +0900114 private static final String DPDK_DEVARGS = "dpdk-devargs";
Daniel Parkc4d06402018-05-28 15:57:37 +0900115 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900116 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900117 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900118 private static final int DPID_BEGIN = 3;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected LeadershipService leadershipService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ClusterService clusterService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected DeviceService deviceService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected DeviceAdminService deviceAdminService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected OvsdbController ovsdbController;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900139 protected OpenstackNodeService osNodeService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected OpenstackNodeAdminService osNodeAdminService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected ComponentConfigService componentConfigService;
146
147 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
148 label = "OVSDB server listen port")
149 private int ovsdbPort = DEFAULT_OVSDB_PORT;
150
Jian Li97482c12018-07-03 01:08:23 +0900151 @Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
152 label = "A flag which indicates whether auto-recover openstack " +
153 "node status at the receiving of switch reconnecting event.")
154 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
155
Hyunsun Moon0d457362017-06-27 17:19:41 +0900156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
158
159 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
160 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900161 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
162
163 private ApplicationId appId;
164 private NodeId localNode;
165
166 @Activate
167 protected void activate() {
168 appId = coreService.getAppId(APP_ID);
169 localNode = clusterService.getLocalNode().id();
170
171 componentConfigService.registerProperties(getClass());
172 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900173 deviceService.addListener(ovsdbListener);
174 deviceService.addListener(bridgeListener);
175 osNodeService.addListener(osNodeListener);
176
177 log.info("Started");
178 }
179
180 @Deactivate
181 protected void deactivate() {
182 osNodeService.removeListener(osNodeListener);
183 deviceService.removeListener(bridgeListener);
184 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900185 componentConfigService.unregisterProperties(getClass(), false);
186 leadershipService.withdraw(appId.name());
187 eventExecutor.shutdown();
188
189 log.info("Stopped");
190 }
191
192 @Modified
193 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900194 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900195
196 log.info("Modified");
197 }
198
199 @Override
200 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900201 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900202 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
203 return;
204 }
205 if (!deviceService.isAvailable(osNode.intgBridge())) {
206 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
207 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900208 if (hasDpdkTunnelBridge(osNode)) {
209 createDpdkTunnelBridge(osNode);
210 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900211 }
212
213 @Override
214 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900215 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900216 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900217 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
218 return;
219 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900220
daniel parkb18424c2018-02-05 15:43:43 +0900221 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900222 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
223 osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900224 }
225
226 if (osNode.dataIp() != null &&
227 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
228 createTunnelInterface(osNode);
229 }
230
231 if (osNode.vlanIntf() != null &&
232 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Jian Li97482c12018-07-03 01:08:23 +0900233 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
234 osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900235 }
Jian Lie6312162018-03-21 21:41:00 +0900236
Daniel Parke2658ba2018-08-24 22:33:29 +0900237 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
238 osNode.dpdkConfig().dpdkIntfs().forEach(dpdkInterface ->
239 addOrRemoveDpdkInterface(osNode, dpdkInterface, true));
240 }
241
Jian Lie6312162018-03-21 21:41:00 +0900242 osNode.phyIntfs().forEach(i -> {
243 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900244 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
245 i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900246 }
247 });
248
daniel parkb18424c2018-02-05 15:43:43 +0900249 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900250 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900251 }
252 }
253
254 @Override
255 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900256 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900257 }
258
259 @Override
260 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900261 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900262 }
263
Daniel Parke2658ba2018-08-24 22:33:29 +0900264 private boolean hasDpdkTunnelBridge(OpenstackNode osNode) {
265 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
266 return osNode.dpdkConfig().dpdkIntfs().stream()
267 .anyMatch(intf -> intf.deviceName().equals(TUNNEL_BRIDGE));
268 }
269 return false;
270 }
271
272 private boolean dpdkTunnelBridgeCreated(OpenstackNode osNode) {
273
274 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
275 if (client == null) {
276 log.info("Failed to get ovsdb client");
277 return false;
278 }
279
280 return client.getBridges().stream()
281 .anyMatch(bridge -> bridge.name().equals(TUNNEL_BRIDGE));
282 }
283
Jian Li340165f2018-02-27 10:38:17 +0900284 /**
Jian Li340165f2018-02-27 10:38:17 +0900285 * Creates a bridge with a given name on a given openstack node.
286 *
287 * @param osNode openstack node
288 * @param bridgeName bridge name
289 * @param deviceId device identifier
290 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900291 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
292 Device device = deviceService.getDevice(osNode.ovsdb());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900293
Jian Li789fadb2018-07-10 13:59:47 +0900294 List<ControllerInfo> controllers;
295
296 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
297 controllers = (List<ControllerInfo>) osNode.controllers();
298 } else {
299 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900300 .map(ControllerNode::ip)
301 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900302
Jian Li789fadb2018-07-10 13:59:47 +0900303 controllers = controllerIps.stream()
304 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
305 .collect(Collectors.toList());
306 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900307
308 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900309
Daniel Park92abf312018-08-08 17:01:35 +0900310 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900311 .name(bridgeName)
312 .failMode(BridgeDescription.FailMode.SECURE)
313 .datapathId(dpid)
314 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900315 .controllers(controllers);
316
317 if (osNode.datapathType().equals(NETDEV)) {
Daniel Parke2658ba2018-08-24 22:33:29 +0900318 builder.datapathType(NETDEV.name().toLowerCase());
Daniel Park92abf312018-08-08 17:01:35 +0900319 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900320
321 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900322 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900323 }
324
Daniel Parke2658ba2018-08-24 22:33:29 +0900325 private void createDpdkTunnelBridge(OpenstackNode osNode) {
326 Device device = deviceService.getDevice(osNode.ovsdb());
327
328 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
329 .name(TUNNEL_BRIDGE)
330 .datapathType(NETDEV.name().toLowerCase());
331
332 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
333 bridgeConfig.addBridge(builder.build());
334 }
335
Jian Li340165f2018-02-27 10:38:17 +0900336 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900337 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900338 *
339 * @param osNode openstack node
340 * @param bridgeName bridge name
341 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900342 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900343 */
Jian Li97482c12018-07-03 01:08:23 +0900344 private void addOrRemoveSystemInterface(OpenstackNode osNode,
345 String bridgeName,
346 String intfName,
Daniel Parkc4d06402018-05-28 15:57:37 +0900347 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900348 Device device = deviceService.getDevice(osNode.ovsdb());
349 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900350 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900351 return;
352 }
353 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900354
355 if (addOrRemove) {
356 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
357 } else {
358 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
359 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900360 }
361
Daniel Parke2658ba2018-08-24 22:33:29 +0900362 private void addOrRemoveDpdkInterface(OpenstackNode osNode,
363 DpdkInterface dpdkInterface,
364 boolean addOrRemove) {
365
366 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
367 if (client == null) {
368 log.info("Failed to get ovsdb client");
369 return;
370 }
371
372 if (addOrRemove) {
373 Map<String, String> options = Maps.newHashMap();
374 options.put(DPDK_DEVARGS, dpdkInterface.pciAddress());
375
376 OvsdbInterface.Builder builder = OvsdbInterface.builder()
377 .name(dpdkInterface.intf())
378 .type(OvsdbInterface.Type.DPDK)
379 .mtu(dpdkInterface.mtu())
380 .options(options);
381
382
383 client.createInterface(dpdkInterface.deviceName(), builder.build());
384 } else {
385 client.dropInterface(dpdkInterface.intf());
386 }
387 }
388
Jian Li340165f2018-02-27 10:38:17 +0900389 /**
390 * Creates a tunnel interface in a given openstack node.
391 *
392 * @param osNode openstack node
393 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900394 private void createTunnelInterface(OpenstackNode osNode) {
395 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
396 return;
397 }
398
399 Device device = deviceService.getDevice(osNode.ovsdb());
400 if (device == null || !device.is(InterfaceConfig.class)) {
401 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
402 return;
403 }
404
405 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
406 .deviceId(INTEGRATION_BRIDGE)
407 .ifaceName(DEFAULT_TUNNEL)
408 .type(TunnelDescription.Type.VXLAN)
409 .remote(TunnelEndPoints.flowTunnelEndpoint())
410 .key(TunnelKeys.flowTunnelKey())
411 .build();
412
413 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
414 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
415 }
416
Jian Li340165f2018-02-27 10:38:17 +0900417 /**
418 * Checks whether a given network interface in a given openstack node is enabled or not.
419 *
420 * @param osNode openstack node
421 * @param intf network interface name
422 * @return true if the given interface is enabled, false otherwise
423 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900424 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900425 return deviceService.isAvailable(osNode.intgBridge()) &&
426 deviceService.getPorts(osNode.intgBridge()).stream()
427 .anyMatch(port -> Objects.equals(
428 port.annotations().value(PORT_NAME), intf) &&
429 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900430 }
431
Jian Li340165f2018-02-27 10:38:17 +0900432 /**
433 * Checks whether all requirements for this state are fulfilled or not.
434 *
435 * @param osNode openstack node
436 * @return true if all requirements are fulfilled, false otherwise
437 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900438 private boolean isCurrentStateDone(OpenstackNode osNode) {
439 switch (osNode.state()) {
440 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900441 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
442 return false;
443 }
444
Daniel Parke2658ba2018-08-24 22:33:29 +0900445 boolean initStateDone = deviceService.isAvailable(osNode.intgBridge());
446 if (hasDpdkTunnelBridge(osNode)) {
447 initStateDone = initStateDone && dpdkTunnelBridgeCreated(osNode);
448 }
449 return initStateDone;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900450 case DEVICE_CREATED:
451 if (osNode.dataIp() != null &&
452 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
453 return false;
454 }
455 if (osNode.vlanIntf() != null &&
456 !isIntfEnabled(osNode, osNode.vlanIntf())) {
457 return false;
458 }
daniel parkb18424c2018-02-05 15:43:43 +0900459 if (osNode.type() == GATEWAY &&
460 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900461 return false;
462 }
Daniel Parke2658ba2018-08-24 22:33:29 +0900463 if (osNode.dpdkConfig() != null && osNode.dpdkConfig().dpdkIntfs() != null) {
464 return isDpdkIntfsCreated(osNode, osNode.dpdkConfig().dpdkIntfs());
465 }
Jian Lie6312162018-03-21 21:41:00 +0900466
467 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
468 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
469 return false;
470 }
471 }
472
Hyunsun Moon0d457362017-06-27 17:19:41 +0900473 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900474 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900475 case INCOMPLETE:
476 // always return false
477 // run init CLI to re-trigger node bootstrap
478 return false;
479 default:
480 return true;
481 }
482 }
483
Daniel Parke2658ba2018-08-24 22:33:29 +0900484 private boolean isDpdkIntfsCreated(OpenstackNode osNode, Collection<DpdkInterface> dpdkInterfaces) {
485 OvsdbClientService client = getOvsdbClient(osNode, ovsdbPort, ovsdbController);
486 if (client == null) {
487 log.info("Failed to get ovsdb client");
488 return false;
489 }
490
491 Set<OvsdbPort> ports = client.getPorts();
492
493 for (DpdkInterface dpdkInterface : dpdkInterfaces) {
494 Optional<OvsdbPort> port = ports.stream()
495 .filter(ovsdbPort -> ovsdbPort.portName().value().equals(dpdkInterface.intf()))
496 .findAny();
497
498 if (!port.isPresent()) {
499 return false;
500 }
501 Interface intf = client.getInterface(dpdkInterface.intf());
502 if (intf == null) {
503 return false;
504 }
505
506 OvsdbSet mtu = (OvsdbSet) intf.getMtuColumn().data();
507 if (mtu == null) {
508 return false;
509 }
510
511 OvsdbMap option = (OvsdbMap) intf.getOptionsColumn().data();
512 if (option == null) {
513 return false;
514 }
515
516 if (!mtu.set().contains(dpdkInterface.mtu().intValue()) ||
517 !option.toString().contains(dpdkInterface.pciAddress())) {
518 log.trace("The dpdk interface {} was created but mtu or pci address is different from the config.");
519 return false;
520 }
521 }
522 return true;
523 }
524
Jian Li340165f2018-02-27 10:38:17 +0900525 /**
526 * Configures the openstack node with new state.
527 *
528 * @param osNode openstack node
529 * @param newState a new state
530 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900531 private void setState(OpenstackNode osNode, NodeState newState) {
532 if (osNode.state() == newState) {
533 return;
534 }
535 OpenstackNode updated = osNode.updateState(newState);
536 osNodeAdminService.updateNode(updated);
537 log.info("Changed {} state: {}", osNode.hostname(), newState);
538 }
539
Jian Li340165f2018-02-27 10:38:17 +0900540 /**
541 * Bootstraps a new openstack node.
542 *
543 * @param osNode openstack node
544 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900545 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900546 if (osNode.type() == CONTROLLER) {
547 if (osNode.state() == INIT && checkEndpoint(osNode)) {
548 setState(osNode, COMPLETE);
549 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900550 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900551 if (isCurrentStateDone(osNode)) {
552 setState(osNode, osNode.state().nextState());
553 } else {
Jian Li97482c12018-07-03 01:08:23 +0900554 log.trace("Processing {} state for {}", osNode.state(),
555 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900556 osNode.state().process(this, osNode);
557 }
558 }
559 }
560
561 /**
562 * Checks the validity of the given endpoint.
563 *
564 * @param osNode gateway node
565 * @return validity result
566 */
567 private boolean checkEndpoint(OpenstackNode osNode) {
568 if (osNode == null) {
569 log.warn("Keystone auth info has not been configured. " +
570 "Please specify auth info via network-cfg.json.");
571 return false;
572 }
573
574 OSClient client = getConnectedClient(osNode);
575
576 if (client == null) {
577 return false;
578 } else {
579 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900580 }
581 }
582
Jian Li340165f2018-02-27 10:38:17 +0900583 /**
Jian Li97482c12018-07-03 01:08:23 +0900584 * Extracts properties from the component configuration context.
585 *
586 * @param context the component context
587 */
588 private void readComponentConfiguration(ComponentContext context) {
589 Dictionary<?, ?> properties = context.getProperties();
590
591 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
592 if (ovsdbPortConfigured == null) {
593 ovsdbPort = DEFAULT_OVSDB_PORT;
594 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
595 } else {
596 ovsdbPort = ovsdbPortConfigured;
597 log.info("Configured. OVSDB port is {}", ovsdbPort);
598 }
599
600 Boolean autoRecoveryConfigured =
601 getBooleanProperty(properties, AUTO_RECOVERY);
602 if (autoRecoveryConfigured == null) {
603 autoRecovery = DEFAULT_AUTO_RECOVERY;
604 log.info("Auto recovery flag is NOT " +
605 "configured, default value is {}", autoRecovery);
606 } else {
607 autoRecovery = autoRecoveryConfigured;
608 log.info("Configured. Auto recovery flag is {}", autoRecovery);
609 }
610 }
611
612 /**
Jian Li340165f2018-02-27 10:38:17 +0900613 * An internal OVSDB listener. This listener is used for listening the
614 * network facing events from OVSDB device. If a new OVSDB device is detected,
615 * ONOS tries to bootstrap the openstack node.
616 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900617 private class InternalOvsdbListener implements DeviceListener {
618
619 @Override
620 public boolean isRelevant(DeviceEvent event) {
621 NodeId leader = leadershipService.getLeader(appId.name());
622 return Objects.equals(localNode, leader) &&
623 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900624 osNodeService.node(event.subject().id()) != null &&
625 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900626 }
627
628 @Override
629 public void event(DeviceEvent event) {
630 Device device = event.subject();
631 OpenstackNode osNode = osNodeService.node(device.id());
632
633 switch (event.type()) {
634 case DEVICE_AVAILABILITY_CHANGED:
635 case DEVICE_ADDED:
636 eventExecutor.execute(() -> {
637 if (deviceService.isAvailable(device.id())) {
638 log.debug("OVSDB {} detected", device.id());
639 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900640 }
641 });
642 break;
643 case PORT_ADDED:
644 case PORT_REMOVED:
645 case DEVICE_REMOVED:
646 default:
647 // do nothing
648 break;
649 }
650 }
651 }
652
Jian Li340165f2018-02-27 10:38:17 +0900653 /**
654 * An internal integration bridge listener. This listener is used for
655 * listening the events from integration bridge. To listen the events from
656 * other types of bridge such as provider bridge or tunnel bridge, we need
657 * to augment OpenstackNodeService.node() method.
658 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900659 private class InternalBridgeListener implements DeviceListener {
660
661 @Override
662 public boolean isRelevant(DeviceEvent event) {
663 NodeId leader = leadershipService.getLeader(appId.name());
664 return Objects.equals(localNode, leader) &&
665 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900666 osNodeService.node(event.subject().id()) != null &&
667 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900668 }
669
670 @Override
671 public void event(DeviceEvent event) {
672 Device device = event.subject();
673 OpenstackNode osNode = osNodeService.node(device.id());
674
675 switch (event.type()) {
676 case DEVICE_AVAILABILITY_CHANGED:
677 case DEVICE_ADDED:
678 eventExecutor.execute(() -> {
679 if (deviceService.isAvailable(device.id())) {
680 log.debug("Integration bridge created on {}", osNode.hostname());
681 bootstrapNode(osNode);
682 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900683 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900684 setState(osNode, INCOMPLETE);
685 }
Jian Li97482c12018-07-03 01:08:23 +0900686
687 if (autoRecovery) {
688 if (osNode.state() == INCOMPLETE ||
689 osNode.state() == DEVICE_CREATED) {
690 log.info("Device {} is reconnected", device.id());
691 osNodeAdminService.updateNode(
692 osNode.updateState(NodeState.INIT));
693 }
694 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900695 });
696 break;
697 case PORT_ADDED:
698 eventExecutor.execute(() -> {
699 Port port = event.port();
700 String portName = port.annotations().value(PORT_NAME);
701 if (osNode.state() == DEVICE_CREATED && (
702 Objects.equals(portName, DEFAULT_TUNNEL) ||
703 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900704 Objects.equals(portName, osNode.uplinkPort()) ||
705 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900706 log.debug("Interface {} added to {}",
707 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900708 bootstrapNode(osNode);
709 }
710 });
711 break;
712 case PORT_REMOVED:
713 eventExecutor.execute(() -> {
714 Port port = event.port();
715 String portName = port.annotations().value(PORT_NAME);
716 if (osNode.state() == COMPLETE && (
717 Objects.equals(portName, DEFAULT_TUNNEL) ||
718 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900719 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Lie6312162018-03-21 21:41:00 +0900720 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900721 log.warn("Interface {} removed from {}",
722 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900723 setState(osNode, INCOMPLETE);
724 }
725 });
726 break;
727 case PORT_UPDATED:
728 case DEVICE_REMOVED:
729 default:
730 // do nothing
731 break;
732 }
733 }
734 }
735
Jian Li340165f2018-02-27 10:38:17 +0900736 /**
Jian Lie6312162018-03-21 21:41:00 +0900737 * Checks whether the openstack node contains the given physical interface.
738 *
739 * @param osNode openstack node
740 * @param portName physical interface
741 * @return true if openstack node contains the given physical interface,
742 * false otherwise
743 */
744 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
745 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
746 if (Objects.equals(portName, phyIntf.intf())) {
747 return true;
748 }
749 }
750
751 return false;
752 }
753
754 /**
Jian Li340165f2018-02-27 10:38:17 +0900755 * An internal openstack node listener.
756 * The notification is triggered by OpenstackNodeStore.
757 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900758 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
759
760 @Override
761 public boolean isRelevant(OpenstackNodeEvent event) {
762 NodeId leader = leadershipService.getLeader(appId.name());
763 return Objects.equals(localNode, leader);
764 }
765
766 @Override
767 public void event(OpenstackNodeEvent event) {
768 switch (event.type()) {
769 case OPENSTACK_NODE_CREATED:
770 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900771 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900772 break;
773 case OPENSTACK_NODE_COMPLETE:
774 break;
775 case OPENSTACK_NODE_REMOVED:
776 break;
777 default:
778 break;
779 }
780 }
781 }
782}