blob: ba7e8ec55d2aa9f26a620c3e08f120a12108ea45 [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;
Jian Li51b844c2018-05-31 10:59:03 +090063import org.openstack4j.api.OSClient;
Hyunsun Moon0d457362017-06-27 17:19:41 +090064import org.osgi.service.component.ComponentContext;
65import org.slf4j.Logger;
66
67import java.util.Dictionary;
68import java.util.List;
69import java.util.Objects;
70import java.util.Set;
71import java.util.concurrent.ExecutorService;
72import java.util.stream.Collectors;
73
Hyunsun Moon0d457362017-06-27 17:19:41 +090074import static java.util.concurrent.Executors.newSingleThreadExecutor;
75import static org.onlab.packet.TpPort.tpPort;
76import static org.onlab.util.Tools.groupedThreads;
77import static org.onosproject.net.AnnotationKeys.PORT_NAME;
78import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Jian Li5afbea42018-02-28 10:37:03 +090079import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
80import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
81import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
82import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
83import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090084import static org.onosproject.openstacknode.api.NodeState.INIT;
85import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090086import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
87import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Jian Li51b844c2018-05-31 10:59:03 +090088import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090089import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090090import static org.slf4j.LoggerFactory.getLogger;
91
92/**
93 * Service bootstraps openstack node based on its type.
94 */
95@Component(immediate = true)
96public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
97
Jian Li5afbea42018-02-28 10:37:03 +090098 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +090099
100 private static final String OVSDB_PORT = "ovsdbPortNum";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900101 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900102 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103 private static final int DEFAULT_OFPORT = 6653;
104 private static final int DPID_BEGIN = 3;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected LeadershipService leadershipService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected ClusterService clusterService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceService deviceService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected DeviceAdminService deviceAdminService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OvsdbController ovsdbController;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900125 protected OpenstackNodeService osNodeService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected OpenstackNodeAdminService osNodeAdminService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ComponentConfigService componentConfigService;
132
133 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
134 label = "OVSDB server listen port")
135 private int ovsdbPort = DEFAULT_OVSDB_PORT;
136
137 private final ExecutorService eventExecutor = newSingleThreadExecutor(
138 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
139
140 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
141 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900142 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
143
144 private ApplicationId appId;
145 private NodeId localNode;
146
147 @Activate
148 protected void activate() {
149 appId = coreService.getAppId(APP_ID);
150 localNode = clusterService.getLocalNode().id();
151
152 componentConfigService.registerProperties(getClass());
153 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900154 deviceService.addListener(ovsdbListener);
155 deviceService.addListener(bridgeListener);
156 osNodeService.addListener(osNodeListener);
157
158 log.info("Started");
159 }
160
161 @Deactivate
162 protected void deactivate() {
163 osNodeService.removeListener(osNodeListener);
164 deviceService.removeListener(bridgeListener);
165 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900166 componentConfigService.unregisterProperties(getClass(), false);
167 leadershipService.withdraw(appId.name());
168 eventExecutor.shutdown();
169
170 log.info("Stopped");
171 }
172
173 @Modified
174 protected void modified(ComponentContext context) {
175 Dictionary<?, ?> properties = context.getProperties();
176 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
177 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
178 ovsdbPort = updatedOvsdbPort;
179 }
180
181 log.info("Modified");
182 }
183
184 @Override
185 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900186 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900187 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
188 return;
189 }
190 if (!deviceService.isAvailable(osNode.intgBridge())) {
191 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
192 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900193 }
194
195 @Override
196 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900197 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900198 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900199 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
200 return;
201 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900202
daniel parkb18424c2018-02-05 15:43:43 +0900203 if (osNode.type() == GATEWAY) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900204 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900205 }
206
207 if (osNode.dataIp() != null &&
208 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
209 createTunnelInterface(osNode);
210 }
211
212 if (osNode.vlanIntf() != null &&
213 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900214 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900215 }
Jian Lie6312162018-03-21 21:41:00 +0900216
217 osNode.phyIntfs().forEach(i -> {
218 if (!isIntfEnabled(osNode, i.intf())) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900219 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900220 }
221 });
222
daniel parkb18424c2018-02-05 15:43:43 +0900223 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900224 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900225 }
226 }
227
228 @Override
229 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900230 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900231 }
232
233 @Override
234 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900235 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900236 }
237
Jian Li340165f2018-02-27 10:38:17 +0900238 /**
Jian Li340165f2018-02-27 10:38:17 +0900239 * Creates a bridge with a given name on a given openstack node.
240 *
241 * @param osNode openstack node
242 * @param bridgeName bridge name
243 * @param deviceId device identifier
244 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900245 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
246 Device device = deviceService.getDevice(osNode.ovsdb());
247 if (device == null || !device.is(BridgeConfig.class)) {
248 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
249 return;
250 }
251
daniel parkb18424c2018-02-05 15:43:43 +0900252 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900253 .map(ControllerNode::ip)
254 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900255
256 List<ControllerInfo> controllers = controllerIps.stream()
257 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
258 .collect(Collectors.toList());
259
260 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900261
Hyunsun Moon0d457362017-06-27 17:19:41 +0900262 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
263 .name(bridgeName)
264 .failMode(BridgeDescription.FailMode.SECURE)
265 .datapathId(dpid)
266 .disableInBand()
267 .controllers(controllers)
268 .build();
269
270 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
271 bridgeConfig.addBridge(bridgeDesc);
272 }
273
Jian Li340165f2018-02-27 10:38:17 +0900274 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900275 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900276 *
277 * @param osNode openstack node
278 * @param bridgeName bridge name
279 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900280 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900281 */
Daniel Parkc4d06402018-05-28 15:57:37 +0900282 private void addOrRemoveSystemInterface(OpenstackNode osNode, String bridgeName, String intfName,
283 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900284 Device device = deviceService.getDevice(osNode.ovsdb());
285 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900286 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900287 return;
288 }
289 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900290
291 if (addOrRemove) {
292 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
293 } else {
294 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
295 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900296 }
297
Jian Li340165f2018-02-27 10:38:17 +0900298 /**
299 * Creates a tunnel interface in a given openstack node.
300 *
301 * @param osNode openstack node
302 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900303 private void createTunnelInterface(OpenstackNode osNode) {
304 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
305 return;
306 }
307
308 Device device = deviceService.getDevice(osNode.ovsdb());
309 if (device == null || !device.is(InterfaceConfig.class)) {
310 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
311 return;
312 }
313
314 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
315 .deviceId(INTEGRATION_BRIDGE)
316 .ifaceName(DEFAULT_TUNNEL)
317 .type(TunnelDescription.Type.VXLAN)
318 .remote(TunnelEndPoints.flowTunnelEndpoint())
319 .key(TunnelKeys.flowTunnelKey())
320 .build();
321
322 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
323 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
324 }
325
Hyunsun Moon0d457362017-06-27 17:19:41 +0900326 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
327 Device device = deviceService.getDevice(deviceId);
328 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
329 log.error("The extension treatment is not supported");
330 return null;
331 }
332
Ray Milkey74e59132018-01-17 15:24:52 -0800333 if (device == null) {
334 return null;
335 }
336
Hyunsun Moon0d457362017-06-27 17:19:41 +0900337 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
338 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
339 try {
340 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
341 return treatment;
342 } catch (ExtensionPropertyException e) {
343 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
344 return null;
345 }
346 }
347
Jian Li340165f2018-02-27 10:38:17 +0900348 /**
349 * Checks whether a given network interface in a given openstack node is enabled or not.
350 *
351 * @param osNode openstack node
352 * @param intf network interface name
353 * @return true if the given interface is enabled, false otherwise
354 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900355 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900356 return deviceService.isAvailable(osNode.intgBridge()) &&
357 deviceService.getPorts(osNode.intgBridge()).stream()
358 .anyMatch(port -> Objects.equals(
359 port.annotations().value(PORT_NAME), intf) &&
360 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900361 }
362
Jian Li340165f2018-02-27 10:38:17 +0900363 /**
364 * Checks whether all requirements for this state are fulfilled or not.
365 *
366 * @param osNode openstack node
367 * @return true if all requirements are fulfilled, false otherwise
368 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900369 private boolean isCurrentStateDone(OpenstackNode osNode) {
370 switch (osNode.state()) {
371 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900372 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
373 return false;
374 }
375
Jian Li5afbea42018-02-28 10:37:03 +0900376 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900377 case DEVICE_CREATED:
378 if (osNode.dataIp() != null &&
379 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
380 return false;
381 }
382 if (osNode.vlanIntf() != null &&
383 !isIntfEnabled(osNode, osNode.vlanIntf())) {
384 return false;
385 }
daniel parkb18424c2018-02-05 15:43:43 +0900386 if (osNode.type() == GATEWAY &&
387 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900388 return false;
389 }
Jian Lie6312162018-03-21 21:41:00 +0900390
391 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
392 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
393 return false;
394 }
395 }
396
Hyunsun Moon0d457362017-06-27 17:19:41 +0900397 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900398 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900399 case INCOMPLETE:
400 // always return false
401 // run init CLI to re-trigger node bootstrap
402 return false;
403 default:
404 return true;
405 }
406 }
407
Jian Li340165f2018-02-27 10:38:17 +0900408 /**
409 * Configures the openstack node with new state.
410 *
411 * @param osNode openstack node
412 * @param newState a new state
413 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900414 private void setState(OpenstackNode osNode, NodeState newState) {
415 if (osNode.state() == newState) {
416 return;
417 }
418 OpenstackNode updated = osNode.updateState(newState);
419 osNodeAdminService.updateNode(updated);
420 log.info("Changed {} state: {}", osNode.hostname(), newState);
421 }
422
Jian Li340165f2018-02-27 10:38:17 +0900423 /**
424 * Bootstraps a new openstack node.
425 *
426 * @param osNode openstack node
427 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900428 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900429 if (osNode.type() == CONTROLLER) {
430 if (osNode.state() == INIT && checkEndpoint(osNode)) {
431 setState(osNode, COMPLETE);
432 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900433 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900434 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 }
442
443 /**
444 * Checks the validity of the given endpoint.
445 *
446 * @param osNode gateway node
447 * @return validity result
448 */
449 private boolean checkEndpoint(OpenstackNode osNode) {
450 if (osNode == null) {
451 log.warn("Keystone auth info has not been configured. " +
452 "Please specify auth info via network-cfg.json.");
453 return false;
454 }
455
456 OSClient client = getConnectedClient(osNode);
457
458 if (client == null) {
459 return false;
460 } else {
461 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900462 }
463 }
464
Jian Li340165f2018-02-27 10:38:17 +0900465 /**
466 * An internal OVSDB listener. This listener is used for listening the
467 * network facing events from OVSDB device. If a new OVSDB device is detected,
468 * ONOS tries to bootstrap the openstack node.
469 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900470 private class InternalOvsdbListener implements DeviceListener {
471
472 @Override
473 public boolean isRelevant(DeviceEvent event) {
474 NodeId leader = leadershipService.getLeader(appId.name());
475 return Objects.equals(localNode, leader) &&
476 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900477 osNodeService.node(event.subject().id()) != null &&
478 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900479 }
480
481 @Override
482 public void event(DeviceEvent event) {
483 Device device = event.subject();
484 OpenstackNode osNode = osNodeService.node(device.id());
485
486 switch (event.type()) {
487 case DEVICE_AVAILABILITY_CHANGED:
488 case DEVICE_ADDED:
489 eventExecutor.execute(() -> {
490 if (deviceService.isAvailable(device.id())) {
491 log.debug("OVSDB {} detected", device.id());
492 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900493 }
494 });
495 break;
496 case PORT_ADDED:
497 case PORT_REMOVED:
498 case DEVICE_REMOVED:
499 default:
500 // do nothing
501 break;
502 }
503 }
504 }
505
Jian Li340165f2018-02-27 10:38:17 +0900506 /**
507 * An internal integration bridge listener. This listener is used for
508 * listening the events from integration bridge. To listen the events from
509 * other types of bridge such as provider bridge or tunnel bridge, we need
510 * to augment OpenstackNodeService.node() method.
511 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900512 private class InternalBridgeListener implements DeviceListener {
513
514 @Override
515 public boolean isRelevant(DeviceEvent event) {
516 NodeId leader = leadershipService.getLeader(appId.name());
517 return Objects.equals(localNode, leader) &&
518 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900519 osNodeService.node(event.subject().id()) != null &&
520 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900521 }
522
523 @Override
524 public void event(DeviceEvent event) {
525 Device device = event.subject();
526 OpenstackNode osNode = osNodeService.node(device.id());
527
528 switch (event.type()) {
529 case DEVICE_AVAILABILITY_CHANGED:
530 case DEVICE_ADDED:
531 eventExecutor.execute(() -> {
532 if (deviceService.isAvailable(device.id())) {
533 log.debug("Integration bridge created on {}", osNode.hostname());
534 bootstrapNode(osNode);
535 } else if (osNode.state() == COMPLETE) {
536 log.warn("Device {} disconnected", device.id());
537 setState(osNode, INCOMPLETE);
538 }
539 });
540 break;
541 case PORT_ADDED:
542 eventExecutor.execute(() -> {
543 Port port = event.port();
544 String portName = port.annotations().value(PORT_NAME);
545 if (osNode.state() == DEVICE_CREATED && (
546 Objects.equals(portName, DEFAULT_TUNNEL) ||
547 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900548 Objects.equals(portName, osNode.uplinkPort()) ||
549 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900550 log.debug("Interface {} added to {}", portName, event.subject().id());
551 bootstrapNode(osNode);
552 }
553 });
554 break;
555 case PORT_REMOVED:
556 eventExecutor.execute(() -> {
557 Port port = event.port();
558 String portName = port.annotations().value(PORT_NAME);
559 if (osNode.state() == COMPLETE && (
560 Objects.equals(portName, DEFAULT_TUNNEL) ||
561 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900562 Objects.equals(portName, osNode.uplinkPort()) ||
563 containsPhyIntf(osNode, portName))) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900564 log.warn("Interface {} removed from {}", portName, event.subject().id());
565 setState(osNode, INCOMPLETE);
566 }
567 });
568 break;
569 case PORT_UPDATED:
570 case DEVICE_REMOVED:
571 default:
572 // do nothing
573 break;
574 }
575 }
576 }
577
Jian Li340165f2018-02-27 10:38:17 +0900578 /**
Jian Lie6312162018-03-21 21:41:00 +0900579 * Checks whether the openstack node contains the given physical interface.
580 *
581 * @param osNode openstack node
582 * @param portName physical interface
583 * @return true if openstack node contains the given physical interface,
584 * false otherwise
585 */
586 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
587 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
588 if (Objects.equals(portName, phyIntf.intf())) {
589 return true;
590 }
591 }
592
593 return false;
594 }
595
596 /**
Jian Li340165f2018-02-27 10:38:17 +0900597 * An internal openstack node listener.
598 * The notification is triggered by OpenstackNodeStore.
599 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900600 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
601
602 @Override
603 public boolean isRelevant(OpenstackNodeEvent event) {
604 NodeId leader = leadershipService.getLeader(appId.name());
605 return Objects.equals(localNode, leader);
606 }
607
608 @Override
609 public void event(OpenstackNodeEvent event) {
610 switch (event.type()) {
611 case OPENSTACK_NODE_CREATED:
612 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900613 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900614 break;
615 case OPENSTACK_NODE_COMPLETE:
616 break;
617 case OPENSTACK_NODE_REMOVED:
618 break;
619 default:
620 break;
621 }
622 }
623 }
624}