blob: 6064267ff607afdd93015ba37ad0dfa59726b2d1 [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
Hyunsun Moon0d457362017-06-27 17:19:41 +090018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.IpAddress;
26import org.onlab.util.Tools;
27import org.onosproject.cfg.ComponentConfigService;
28import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.ControllerNode;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090034import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.Port;
37import org.onosproject.net.behaviour.BridgeConfig;
38import org.onosproject.net.behaviour.BridgeDescription;
39import org.onosproject.net.behaviour.BridgeName;
40import org.onosproject.net.behaviour.ControllerInfo;
41import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon0d457362017-06-27 17:19:41 +090042import org.onosproject.net.behaviour.DefaultTunnelDescription;
43import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
44import 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;
Hyunsun Moon0d457362017-06-27 17:19:41 +090052import org.onosproject.net.flow.instructions.ExtensionPropertyException;
53import org.onosproject.net.flow.instructions.ExtensionTreatment;
Hyunsun Moon0d457362017-06-27 17:19:41 +090054import org.onosproject.openstacknode.api.NodeState;
55import org.onosproject.openstacknode.api.OpenstackNode;
Hyunsun Moon0d457362017-06-27 17:19:41 +090056import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
57import org.onosproject.openstacknode.api.OpenstackNodeEvent;
58import org.onosproject.openstacknode.api.OpenstackNodeHandler;
59import org.onosproject.openstacknode.api.OpenstackNodeListener;
60import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lie6312162018-03-21 21:41:00 +090061import org.onosproject.openstacknode.api.OpenstackPhyInterface;
Hyunsun Moon0d457362017-06-27 17:19:41 +090062import org.onosproject.ovsdb.controller.OvsdbController;
Hyunsun Moon0d457362017-06-27 17:19:41 +090063import org.osgi.service.component.ComponentContext;
64import org.slf4j.Logger;
65
66import java.util.Dictionary;
67import java.util.List;
68import java.util.Objects;
69import java.util.Set;
70import java.util.concurrent.ExecutorService;
71import java.util.stream.Collectors;
72
Hyunsun Moon0d457362017-06-27 17:19:41 +090073import static java.util.concurrent.Executors.newSingleThreadExecutor;
74import static org.onlab.packet.TpPort.tpPort;
75import static org.onlab.util.Tools.groupedThreads;
76import static org.onosproject.net.AnnotationKeys.PORT_NAME;
77import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Jian Li5afbea42018-02-28 10:37:03 +090078import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
79import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
80import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
81import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
82import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Hyunsun Moon0d457362017-06-27 17:19:41 +090083import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
84import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Daniel Parkc4d06402018-05-28 15:57:37 +090085import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090086import static org.slf4j.LoggerFactory.getLogger;
87
88/**
89 * Service bootstraps openstack node based on its type.
90 */
91@Component(immediate = true)
92public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
93
Jian Li5afbea42018-02-28 10:37:03 +090094 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +090095
96 private static final String OVSDB_PORT = "ovsdbPortNum";
Hyunsun Moon0d457362017-06-27 17:19:41 +090097 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +090098 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +090099 private static final int DEFAULT_OFPORT = 6653;
100 private static final int DPID_BEGIN = 3;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected CoreService coreService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected LeadershipService leadershipService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected ClusterService clusterService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected DeviceAdminService deviceAdminService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected OvsdbController ovsdbController;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900121 protected OpenstackNodeService osNodeService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected OpenstackNodeAdminService osNodeAdminService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService componentConfigService;
128
129 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
130 label = "OVSDB server listen port")
131 private int ovsdbPort = DEFAULT_OVSDB_PORT;
132
133 private final ExecutorService eventExecutor = newSingleThreadExecutor(
134 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
135
136 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
137 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900138 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
139
140 private ApplicationId appId;
141 private NodeId localNode;
142
143 @Activate
144 protected void activate() {
145 appId = coreService.getAppId(APP_ID);
146 localNode = clusterService.getLocalNode().id();
147
148 componentConfigService.registerProperties(getClass());
149 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900150 deviceService.addListener(ovsdbListener);
151 deviceService.addListener(bridgeListener);
152 osNodeService.addListener(osNodeListener);
153
154 log.info("Started");
155 }
156
157 @Deactivate
158 protected void deactivate() {
159 osNodeService.removeListener(osNodeListener);
160 deviceService.removeListener(bridgeListener);
161 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900162 componentConfigService.unregisterProperties(getClass(), false);
163 leadershipService.withdraw(appId.name());
164 eventExecutor.shutdown();
165
166 log.info("Stopped");
167 }
168
169 @Modified
170 protected void modified(ComponentContext context) {
171 Dictionary<?, ?> properties = context.getProperties();
172 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
173 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
174 ovsdbPort = updatedOvsdbPort;
175 }
176
177 log.info("Modified");
178 }
179
180 @Override
181 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900182 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900183 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
184 return;
185 }
186 if (!deviceService.isAvailable(osNode.intgBridge())) {
187 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
188 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900189 }
190
191 @Override
192 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900193 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900194 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900195 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
196 return;
197 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900198
daniel parkb18424c2018-02-05 15:43:43 +0900199 if (osNode.type() == GATEWAY) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900200 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900201 }
202
203 if (osNode.dataIp() != null &&
204 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
205 createTunnelInterface(osNode);
206 }
207
208 if (osNode.vlanIntf() != null &&
209 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900210 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900211 }
Jian Lie6312162018-03-21 21:41:00 +0900212
213 osNode.phyIntfs().forEach(i -> {
214 if (!isIntfEnabled(osNode, i.intf())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900215 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900216 }
217 });
218
daniel parkb18424c2018-02-05 15:43:43 +0900219 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900220 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900221 }
222 }
223
224 @Override
225 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900226 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900227 }
228
229 @Override
230 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900231 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900232 }
233
Jian Li340165f2018-02-27 10:38:17 +0900234 /**
Jian Li340165f2018-02-27 10:38:17 +0900235 * Creates a bridge with a given name on a given openstack node.
236 *
237 * @param osNode openstack node
238 * @param bridgeName bridge name
239 * @param deviceId device identifier
240 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900241 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
242 Device device = deviceService.getDevice(osNode.ovsdb());
243 if (device == null || !device.is(BridgeConfig.class)) {
244 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
245 return;
246 }
247
daniel parkb18424c2018-02-05 15:43:43 +0900248 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900249 .map(ControllerNode::ip)
250 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900251
252 List<ControllerInfo> controllers = controllerIps.stream()
253 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
254 .collect(Collectors.toList());
255
256 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900257
Hyunsun Moon0d457362017-06-27 17:19:41 +0900258 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
259 .name(bridgeName)
260 .failMode(BridgeDescription.FailMode.SECURE)
261 .datapathId(dpid)
262 .disableInBand()
263 .controllers(controllers)
264 .build();
265
266 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
267 bridgeConfig.addBridge(bridgeDesc);
268 }
269
Jian Li340165f2018-02-27 10:38:17 +0900270 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900271 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900272 *
273 * @param osNode openstack node
274 * @param bridgeName bridge name
275 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900276 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900277 */
Daniel Parkc4d06402018-05-28 15:57:37 +0900278 private void addOrRemoveSystemInterface(OpenstackNode osNode, String bridgeName, String intfName,
279 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900280 Device device = deviceService.getDevice(osNode.ovsdb());
281 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900282 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900283 return;
284 }
285 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900286
287 if (addOrRemove) {
288 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
289 } else {
290 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
291 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900292 }
293
Jian Li340165f2018-02-27 10:38:17 +0900294 /**
295 * Creates a tunnel interface in a given openstack node.
296 *
297 * @param osNode openstack node
298 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900299 private void createTunnelInterface(OpenstackNode osNode) {
300 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
301 return;
302 }
303
304 Device device = deviceService.getDevice(osNode.ovsdb());
305 if (device == null || !device.is(InterfaceConfig.class)) {
306 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
307 return;
308 }
309
310 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
311 .deviceId(INTEGRATION_BRIDGE)
312 .ifaceName(DEFAULT_TUNNEL)
313 .type(TunnelDescription.Type.VXLAN)
314 .remote(TunnelEndPoints.flowTunnelEndpoint())
315 .key(TunnelKeys.flowTunnelKey())
316 .build();
317
318 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
319 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
320 }
321
Hyunsun Moon0d457362017-06-27 17:19:41 +0900322 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
323 Device device = deviceService.getDevice(deviceId);
324 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
325 log.error("The extension treatment is not supported");
326 return null;
327 }
328
Ray Milkey74e59132018-01-17 15:24:52 -0800329 if (device == null) {
330 return null;
331 }
332
Hyunsun Moon0d457362017-06-27 17:19:41 +0900333 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
334 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
335 try {
336 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
337 return treatment;
338 } catch (ExtensionPropertyException e) {
339 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
340 return null;
341 }
342 }
343
Jian Li340165f2018-02-27 10:38:17 +0900344 /**
345 * Checks whether a given network interface in a given openstack node is enabled or not.
346 *
347 * @param osNode openstack node
348 * @param intf network interface name
349 * @return true if the given interface is enabled, false otherwise
350 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900351 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900352 return deviceService.isAvailable(osNode.intgBridge()) &&
353 deviceService.getPorts(osNode.intgBridge()).stream()
354 .anyMatch(port -> Objects.equals(
355 port.annotations().value(PORT_NAME), intf) &&
356 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900357 }
358
Jian Li340165f2018-02-27 10:38:17 +0900359 /**
360 * Checks whether all requirements for this state are fulfilled or not.
361 *
362 * @param osNode openstack node
363 * @return true if all requirements are fulfilled, false otherwise
364 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900365 private boolean isCurrentStateDone(OpenstackNode osNode) {
366 switch (osNode.state()) {
367 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900368 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
369 return false;
370 }
371
Jian Li5afbea42018-02-28 10:37:03 +0900372 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900373 case DEVICE_CREATED:
374 if (osNode.dataIp() != null &&
375 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
376 return false;
377 }
378 if (osNode.vlanIntf() != null &&
379 !isIntfEnabled(osNode, osNode.vlanIntf())) {
380 return false;
381 }
daniel parkb18424c2018-02-05 15:43:43 +0900382 if (osNode.type() == GATEWAY &&
383 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900384 return false;
385 }
Jian Lie6312162018-03-21 21:41:00 +0900386
387 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
388 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
389 return false;
390 }
391 }
392
Hyunsun Moon0d457362017-06-27 17:19:41 +0900393 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900394 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900395 case INCOMPLETE:
396 // always return false
397 // run init CLI to re-trigger node bootstrap
398 return false;
399 default:
400 return true;
401 }
402 }
403
Jian Li340165f2018-02-27 10:38:17 +0900404 /**
405 * Configures the openstack node with new state.
406 *
407 * @param osNode openstack node
408 * @param newState a new state
409 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900410 private void setState(OpenstackNode osNode, NodeState newState) {
411 if (osNode.state() == newState) {
412 return;
413 }
414 OpenstackNode updated = osNode.updateState(newState);
415 osNodeAdminService.updateNode(updated);
416 log.info("Changed {} state: {}", osNode.hostname(), newState);
417 }
418
Jian Li340165f2018-02-27 10:38:17 +0900419 /**
420 * Bootstraps a new openstack node.
421 *
422 * @param osNode openstack node
423 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900424 private void bootstrapNode(OpenstackNode osNode) {
425 if (isCurrentStateDone(osNode)) {
426 setState(osNode, osNode.state().nextState());
427 } else {
428 log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
429 osNode.state().process(this, osNode);
430 }
431 }
432
Jian Li340165f2018-02-27 10:38:17 +0900433 /**
434 * An internal OVSDB listener. This listener is used for listening the
435 * network facing events from OVSDB device. If a new OVSDB device is detected,
436 * ONOS tries to bootstrap the openstack node.
437 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900438 private class InternalOvsdbListener implements DeviceListener {
439
440 @Override
441 public boolean isRelevant(DeviceEvent event) {
442 NodeId leader = leadershipService.getLeader(appId.name());
443 return Objects.equals(localNode, leader) &&
444 event.subject().type() == Device.Type.CONTROLLER &&
445 osNodeService.node(event.subject().id()) != null;
446 }
447
448 @Override
449 public void event(DeviceEvent event) {
450 Device device = event.subject();
451 OpenstackNode osNode = osNodeService.node(device.id());
452
453 switch (event.type()) {
454 case DEVICE_AVAILABILITY_CHANGED:
455 case DEVICE_ADDED:
456 eventExecutor.execute(() -> {
457 if (deviceService.isAvailable(device.id())) {
458 log.debug("OVSDB {} detected", device.id());
459 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900460 }
461 });
462 break;
463 case PORT_ADDED:
464 case PORT_REMOVED:
465 case DEVICE_REMOVED:
466 default:
467 // do nothing
468 break;
469 }
470 }
471 }
472
Jian Li340165f2018-02-27 10:38:17 +0900473 /**
474 * An internal integration bridge listener. This listener is used for
475 * listening the events from integration bridge. To listen the events from
476 * other types of bridge such as provider bridge or tunnel bridge, we need
477 * to augment OpenstackNodeService.node() method.
478 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900479 private class InternalBridgeListener implements DeviceListener {
480
481 @Override
482 public boolean isRelevant(DeviceEvent event) {
483 NodeId leader = leadershipService.getLeader(appId.name());
484 return Objects.equals(localNode, leader) &&
485 event.subject().type() == Device.Type.SWITCH &&
486 osNodeService.node(event.subject().id()) != null;
487 }
488
489 @Override
490 public void event(DeviceEvent event) {
491 Device device = event.subject();
492 OpenstackNode osNode = osNodeService.node(device.id());
493
494 switch (event.type()) {
495 case DEVICE_AVAILABILITY_CHANGED:
496 case DEVICE_ADDED:
497 eventExecutor.execute(() -> {
498 if (deviceService.isAvailable(device.id())) {
499 log.debug("Integration bridge created on {}", osNode.hostname());
500 bootstrapNode(osNode);
501 } else if (osNode.state() == COMPLETE) {
502 log.warn("Device {} disconnected", device.id());
503 setState(osNode, INCOMPLETE);
504 }
505 });
506 break;
507 case PORT_ADDED:
508 eventExecutor.execute(() -> {
509 Port port = event.port();
510 String portName = port.annotations().value(PORT_NAME);
511 if (osNode.state() == DEVICE_CREATED && (
512 Objects.equals(portName, DEFAULT_TUNNEL) ||
513 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900514 Objects.equals(portName, osNode.uplinkPort()) ||
515 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900516 log.debug("Interface {} added to {}", portName, event.subject().id());
517 bootstrapNode(osNode);
518 }
519 });
520 break;
521 case PORT_REMOVED:
522 eventExecutor.execute(() -> {
523 Port port = event.port();
524 String portName = port.annotations().value(PORT_NAME);
525 if (osNode.state() == COMPLETE && (
526 Objects.equals(portName, DEFAULT_TUNNEL) ||
527 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900528 Objects.equals(portName, osNode.uplinkPort()) ||
529 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900530 log.warn("Interface {} removed from {}", portName, event.subject().id());
531 setState(osNode, INCOMPLETE);
532 }
533 });
534 break;
535 case PORT_UPDATED:
536 case DEVICE_REMOVED:
537 default:
538 // do nothing
539 break;
540 }
541 }
542 }
543
Jian Li340165f2018-02-27 10:38:17 +0900544 /**
Jian Lie6312162018-03-21 21:41:00 +0900545 * Checks whether the openstack node contains the given physical interface.
546 *
547 * @param osNode openstack node
548 * @param portName physical interface
549 * @return true if openstack node contains the given physical interface,
550 * false otherwise
551 */
552 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
553 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
554 if (Objects.equals(portName, phyIntf.intf())) {
555 return true;
556 }
557 }
558
559 return false;
560 }
561
562 /**
Jian Li340165f2018-02-27 10:38:17 +0900563 * An internal openstack node listener.
564 * The notification is triggered by OpenstackNodeStore.
565 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900566 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
567
568 @Override
569 public boolean isRelevant(OpenstackNodeEvent event) {
570 NodeId leader = leadershipService.getLeader(appId.name());
571 return Objects.equals(localNode, leader);
572 }
573
574 @Override
575 public void event(OpenstackNodeEvent event) {
576 switch (event.type()) {
577 case OPENSTACK_NODE_CREATED:
578 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900579 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900580 break;
581 case OPENSTACK_NODE_COMPLETE:
582 break;
583 case OPENSTACK_NODE_REMOVED:
584 break;
585 default:
586 break;
587 }
588 }
589 }
590}