blob: e8de7a0b21947073375b598b4b50257458654d1b [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.OvsdbClientService;
63import org.onosproject.ovsdb.controller.OvsdbController;
64import org.onosproject.ovsdb.controller.OvsdbNodeId;
65import org.osgi.service.component.ComponentContext;
66import org.slf4j.Logger;
67
68import java.util.Dictionary;
69import java.util.List;
70import java.util.Objects;
71import java.util.Set;
72import java.util.concurrent.ExecutorService;
73import java.util.stream.Collectors;
74
Hyunsun Moon0d457362017-06-27 17:19:41 +090075import static java.util.concurrent.Executors.newSingleThreadExecutor;
76import static org.onlab.packet.TpPort.tpPort;
77import static org.onlab.util.Tools.groupedThreads;
78import static org.onosproject.net.AnnotationKeys.PORT_NAME;
79import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Jian Li5afbea42018-02-28 10:37:03 +090080import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
81import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
82import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
83import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
84import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Hyunsun Moon0d457362017-06-27 17:19:41 +090085import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
86import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
87import static org.slf4j.LoggerFactory.getLogger;
88
89/**
90 * Service bootstraps openstack node based on its type.
91 */
92@Component(immediate = true)
93public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
94
Jian Li5afbea42018-02-28 10:37:03 +090095 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +090096
97 private static final String OVSDB_PORT = "ovsdbPortNum";
98 private static final int DEFAULT_OVSDB_PORT = 6640;
99 private static final String DEFAULT_OF_PROTO = "tcp";
100 private static final int DEFAULT_OFPORT = 6653;
101 private static final int DPID_BEGIN = 3;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected CoreService coreService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected LeadershipService leadershipService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ClusterService clusterService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceAdminService deviceAdminService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OvsdbController ovsdbController;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900122 protected OpenstackNodeService osNodeService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackNodeAdminService osNodeAdminService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected ComponentConfigService componentConfigService;
129
130 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
131 label = "OVSDB server listen port")
132 private int ovsdbPort = DEFAULT_OVSDB_PORT;
133
134 private final ExecutorService eventExecutor = newSingleThreadExecutor(
135 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
136
137 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
138 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900139 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
140
141 private ApplicationId appId;
142 private NodeId localNode;
143
144 @Activate
145 protected void activate() {
146 appId = coreService.getAppId(APP_ID);
147 localNode = clusterService.getLocalNode().id();
148
149 componentConfigService.registerProperties(getClass());
150 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900151 deviceService.addListener(ovsdbListener);
152 deviceService.addListener(bridgeListener);
153 osNodeService.addListener(osNodeListener);
Jian Li7d691f32018-05-03 16:56:50 +0900154 ovsdbController.setServerMode(false);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900155
156 log.info("Started");
157 }
158
159 @Deactivate
160 protected void deactivate() {
161 osNodeService.removeListener(osNodeListener);
162 deviceService.removeListener(bridgeListener);
163 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900164 componentConfigService.unregisterProperties(getClass(), false);
165 leadershipService.withdraw(appId.name());
166 eventExecutor.shutdown();
167
168 log.info("Stopped");
169 }
170
171 @Modified
172 protected void modified(ComponentContext context) {
173 Dictionary<?, ?> properties = context.getProperties();
174 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
175 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
176 ovsdbPort = updatedOvsdbPort;
177 }
178
179 log.info("Modified");
180 }
181
182 @Override
183 public void processInitState(OpenstackNode osNode) {
184 if (!isOvsdbConnected(osNode)) {
185 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
186 return;
187 }
188 if (!deviceService.isAvailable(osNode.intgBridge())) {
189 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
190 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900191 }
192
193 @Override
194 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900195 try {
196 if (!isOvsdbConnected(osNode)) {
197 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
198 return;
199 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900200
daniel parkb18424c2018-02-05 15:43:43 +0900201 if (osNode.type() == GATEWAY) {
202 addSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.uplinkPort());
203 }
204
205 if (osNode.dataIp() != null &&
206 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
207 createTunnelInterface(osNode);
208 }
209
210 if (osNode.vlanIntf() != null &&
211 !isIntfEnabled(osNode, osNode.vlanIntf())) {
212 addSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf());
213 }
Jian Lie6312162018-03-21 21:41:00 +0900214
215 osNode.phyIntfs().forEach(i -> {
216 if (!isIntfEnabled(osNode, i.intf())) {
217 addSystemInterface(osNode, INTEGRATION_BRIDGE, i.intf());
218 }
219 });
220
daniel parkb18424c2018-02-05 15:43:43 +0900221 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900222 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900223 }
224 }
225
226 @Override
227 public void processCompleteState(OpenstackNode osNode) {
228 OvsdbClientService ovsdbClient = ovsdbController.getOvsdbClient(
229 new OvsdbNodeId(osNode.managementIp(), DEFAULT_OVSDB_PORT));
230 if (ovsdbClient != null && ovsdbClient.isConnected()) {
231 ovsdbClient.disconnect();
232 }
233 }
234
235 @Override
236 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900237 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900238 }
239
Jian Li340165f2018-02-27 10:38:17 +0900240 /**
241 * Checks whether the controller has a connection with an OVSDB that resides
242 * inside the given openstack node.
243 *
244 * @param osNode openstack node
245 * @return true if the controller is connected to the OVSDB, false otherwise
246 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900247 private boolean isOvsdbConnected(OpenstackNode osNode) {
248 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
249 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
250 return deviceService.isAvailable(osNode.ovsdb()) &&
251 client != null &&
252 client.isConnected();
253 }
254
Jian Li340165f2018-02-27 10:38:17 +0900255 /**
256 * Creates a bridge with a given name on a given openstack node.
257 *
258 * @param osNode openstack node
259 * @param bridgeName bridge name
260 * @param deviceId device identifier
261 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900262 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
263 Device device = deviceService.getDevice(osNode.ovsdb());
264 if (device == null || !device.is(BridgeConfig.class)) {
265 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
266 return;
267 }
268
daniel parkb18424c2018-02-05 15:43:43 +0900269 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900270 .map(ControllerNode::ip)
271 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900272
273 List<ControllerInfo> controllers = controllerIps.stream()
274 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
275 .collect(Collectors.toList());
276
277 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900278
Hyunsun Moon0d457362017-06-27 17:19:41 +0900279 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
280 .name(bridgeName)
281 .failMode(BridgeDescription.FailMode.SECURE)
282 .datapathId(dpid)
283 .disableInBand()
284 .controllers(controllers)
285 .build();
286
287 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
288 bridgeConfig.addBridge(bridgeDesc);
289 }
290
Jian Li340165f2018-02-27 10:38:17 +0900291 /**
292 * Adds a network interface (aka port) into a given bridge of openstack node.
293 *
294 * @param osNode openstack node
295 * @param bridgeName bridge name
296 * @param intfName interface name
297 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900298 private void addSystemInterface(OpenstackNode osNode, String bridgeName, String intfName) {
299 Device device = deviceService.getDevice(osNode.ovsdb());
300 if (device == null || !device.is(BridgeConfig.class)) {
301 return;
302 }
303 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
304 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
305 }
306
Jian Li340165f2018-02-27 10:38:17 +0900307 /**
308 * Creates a tunnel interface in a given openstack node.
309 *
310 * @param osNode openstack node
311 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900312 private void createTunnelInterface(OpenstackNode osNode) {
313 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
314 return;
315 }
316
317 Device device = deviceService.getDevice(osNode.ovsdb());
318 if (device == null || !device.is(InterfaceConfig.class)) {
319 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
320 return;
321 }
322
323 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
324 .deviceId(INTEGRATION_BRIDGE)
325 .ifaceName(DEFAULT_TUNNEL)
326 .type(TunnelDescription.Type.VXLAN)
327 .remote(TunnelEndPoints.flowTunnelEndpoint())
328 .key(TunnelKeys.flowTunnelKey())
329 .build();
330
331 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
332 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
333 }
334
Hyunsun Moon0d457362017-06-27 17:19:41 +0900335 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
336 Device device = deviceService.getDevice(deviceId);
337 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
338 log.error("The extension treatment is not supported");
339 return null;
340 }
341
Ray Milkey74e59132018-01-17 15:24:52 -0800342 if (device == null) {
343 return null;
344 }
345
Hyunsun Moon0d457362017-06-27 17:19:41 +0900346 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
347 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
348 try {
349 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
350 return treatment;
351 } catch (ExtensionPropertyException e) {
352 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
353 return null;
354 }
355 }
356
Jian Li340165f2018-02-27 10:38:17 +0900357 /**
358 * Checks whether a given network interface in a given openstack node is enabled or not.
359 *
360 * @param osNode openstack node
361 * @param intf network interface name
362 * @return true if the given interface is enabled, false otherwise
363 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900364 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900365 return deviceService.isAvailable(osNode.intgBridge()) &&
366 deviceService.getPorts(osNode.intgBridge()).stream()
367 .anyMatch(port -> Objects.equals(
368 port.annotations().value(PORT_NAME), intf) &&
369 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900370 }
371
Jian Li340165f2018-02-27 10:38:17 +0900372 /**
373 * Checks whether all requirements for this state are fulfilled or not.
374 *
375 * @param osNode openstack node
376 * @return true if all requirements are fulfilled, false otherwise
377 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900378 private boolean isCurrentStateDone(OpenstackNode osNode) {
379 switch (osNode.state()) {
380 case INIT:
Jian Li5afbea42018-02-28 10:37:03 +0900381 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900382 case DEVICE_CREATED:
383 if (osNode.dataIp() != null &&
384 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
385 return false;
386 }
387 if (osNode.vlanIntf() != null &&
388 !isIntfEnabled(osNode, osNode.vlanIntf())) {
389 return false;
390 }
daniel parkb18424c2018-02-05 15:43:43 +0900391 if (osNode.type() == GATEWAY &&
392 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900393 return false;
394 }
Jian Lie6312162018-03-21 21:41:00 +0900395
396 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
397 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
398 return false;
399 }
400 }
401
Hyunsun Moon0d457362017-06-27 17:19:41 +0900402 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900403 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900404 case INCOMPLETE:
405 // always return false
406 // run init CLI to re-trigger node bootstrap
407 return false;
408 default:
409 return true;
410 }
411 }
412
Jian Li340165f2018-02-27 10:38:17 +0900413 /**
414 * Configures the openstack node with new state.
415 *
416 * @param osNode openstack node
417 * @param newState a new state
418 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900419 private void setState(OpenstackNode osNode, NodeState newState) {
420 if (osNode.state() == newState) {
421 return;
422 }
423 OpenstackNode updated = osNode.updateState(newState);
424 osNodeAdminService.updateNode(updated);
425 log.info("Changed {} state: {}", osNode.hostname(), newState);
426 }
427
Jian Li340165f2018-02-27 10:38:17 +0900428 /**
429 * Bootstraps a new openstack node.
430 *
431 * @param osNode openstack node
432 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900433 private void bootstrapNode(OpenstackNode osNode) {
434 if (isCurrentStateDone(osNode)) {
435 setState(osNode, osNode.state().nextState());
436 } else {
437 log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
438 osNode.state().process(this, osNode);
439 }
440 }
441
Jian Li340165f2018-02-27 10:38:17 +0900442 /**
443 * An internal OVSDB listener. This listener is used for listening the
444 * network facing events from OVSDB device. If a new OVSDB device is detected,
445 * ONOS tries to bootstrap the openstack node.
446 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900447 private class InternalOvsdbListener implements DeviceListener {
448
449 @Override
450 public boolean isRelevant(DeviceEvent event) {
451 NodeId leader = leadershipService.getLeader(appId.name());
452 return Objects.equals(localNode, leader) &&
453 event.subject().type() == Device.Type.CONTROLLER &&
454 osNodeService.node(event.subject().id()) != null;
455 }
456
457 @Override
458 public void event(DeviceEvent event) {
459 Device device = event.subject();
460 OpenstackNode osNode = osNodeService.node(device.id());
461
462 switch (event.type()) {
463 case DEVICE_AVAILABILITY_CHANGED:
464 case DEVICE_ADDED:
465 eventExecutor.execute(() -> {
466 if (deviceService.isAvailable(device.id())) {
467 log.debug("OVSDB {} detected", device.id());
468 bootstrapNode(osNode);
469 } else if (osNode.state() == COMPLETE) {
470 log.debug("Removing OVSDB {}", device.id());
471 deviceAdminService.removeDevice(device.id());
472 }
473 });
474 break;
475 case PORT_ADDED:
476 case PORT_REMOVED:
477 case DEVICE_REMOVED:
478 default:
479 // do nothing
480 break;
481 }
482 }
483 }
484
Jian Li340165f2018-02-27 10:38:17 +0900485 /**
486 * An internal integration bridge listener. This listener is used for
487 * listening the events from integration bridge. To listen the events from
488 * other types of bridge such as provider bridge or tunnel bridge, we need
489 * to augment OpenstackNodeService.node() method.
490 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900491 private class InternalBridgeListener implements DeviceListener {
492
493 @Override
494 public boolean isRelevant(DeviceEvent event) {
495 NodeId leader = leadershipService.getLeader(appId.name());
496 return Objects.equals(localNode, leader) &&
497 event.subject().type() == Device.Type.SWITCH &&
498 osNodeService.node(event.subject().id()) != null;
499 }
500
501 @Override
502 public void event(DeviceEvent event) {
503 Device device = event.subject();
504 OpenstackNode osNode = osNodeService.node(device.id());
505
506 switch (event.type()) {
507 case DEVICE_AVAILABILITY_CHANGED:
508 case DEVICE_ADDED:
509 eventExecutor.execute(() -> {
510 if (deviceService.isAvailable(device.id())) {
511 log.debug("Integration bridge created on {}", osNode.hostname());
512 bootstrapNode(osNode);
513 } else if (osNode.state() == COMPLETE) {
514 log.warn("Device {} disconnected", device.id());
515 setState(osNode, INCOMPLETE);
516 }
517 });
518 break;
519 case PORT_ADDED:
520 eventExecutor.execute(() -> {
521 Port port = event.port();
522 String portName = port.annotations().value(PORT_NAME);
523 if (osNode.state() == DEVICE_CREATED && (
524 Objects.equals(portName, DEFAULT_TUNNEL) ||
525 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900526 Objects.equals(portName, osNode.uplinkPort()) ||
527 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900528 log.debug("Interface {} added to {}", portName, event.subject().id());
529 bootstrapNode(osNode);
530 }
531 });
532 break;
533 case PORT_REMOVED:
534 eventExecutor.execute(() -> {
535 Port port = event.port();
536 String portName = port.annotations().value(PORT_NAME);
537 if (osNode.state() == COMPLETE && (
538 Objects.equals(portName, DEFAULT_TUNNEL) ||
539 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900540 Objects.equals(portName, osNode.uplinkPort()) ||
541 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900542 log.warn("Interface {} removed from {}", portName, event.subject().id());
543 setState(osNode, INCOMPLETE);
544 }
545 });
546 break;
547 case PORT_UPDATED:
548 case DEVICE_REMOVED:
549 default:
550 // do nothing
551 break;
552 }
553 }
554 }
555
Jian Li340165f2018-02-27 10:38:17 +0900556 /**
Jian Lie6312162018-03-21 21:41:00 +0900557 * Checks whether the openstack node contains the given physical interface.
558 *
559 * @param osNode openstack node
560 * @param portName physical interface
561 * @return true if openstack node contains the given physical interface,
562 * false otherwise
563 */
564 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
565 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
566 if (Objects.equals(portName, phyIntf.intf())) {
567 return true;
568 }
569 }
570
571 return false;
572 }
573
574 /**
Jian Li340165f2018-02-27 10:38:17 +0900575 * An internal openstack node listener.
576 * The notification is triggered by OpenstackNodeStore.
577 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900578 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
579
580 @Override
581 public boolean isRelevant(OpenstackNodeEvent event) {
582 NodeId leader = leadershipService.getLeader(appId.name());
583 return Objects.equals(localNode, leader);
584 }
585
586 @Override
587 public void event(OpenstackNodeEvent event) {
588 switch (event.type()) {
589 case OPENSTACK_NODE_CREATED:
590 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900591 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900592 break;
593 case OPENSTACK_NODE_COMPLETE:
594 break;
595 case OPENSTACK_NODE_REMOVED:
596 break;
597 default:
598 break;
599 }
600 }
601 }
602}