blob: 06b65a335287576541395ec7644a7a44f62f0ec7 [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 Li97482c12018-07-03 01:08:23 +090088import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090089import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090090import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090091import static org.slf4j.LoggerFactory.getLogger;
92
93/**
94 * Service bootstraps openstack node based on its type.
95 */
96@Component(immediate = true)
97public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
98
Jian Li5afbea42018-02-28 10:37:03 +090099 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100
101 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900102 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900104 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900105 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900106 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900107 private static final int DPID_BEGIN = 3;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected CoreService coreService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected LeadershipService leadershipService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected ClusterService clusterService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected DeviceService deviceService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected DeviceAdminService deviceAdminService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OvsdbController ovsdbController;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900128 protected OpenstackNodeService osNodeService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected OpenstackNodeAdminService osNodeAdminService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected ComponentConfigService componentConfigService;
135
136 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
137 label = "OVSDB server listen port")
138 private int ovsdbPort = DEFAULT_OVSDB_PORT;
139
Jian Li97482c12018-07-03 01:08:23 +0900140 @Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
141 label = "A flag which indicates whether auto-recover openstack " +
142 "node status at the receiving of switch reconnecting event.")
143 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
144
Hyunsun Moon0d457362017-06-27 17:19:41 +0900145 private final ExecutorService eventExecutor = newSingleThreadExecutor(
146 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
147
148 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
149 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900150 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
151
152 private ApplicationId appId;
153 private NodeId localNode;
154
155 @Activate
156 protected void activate() {
157 appId = coreService.getAppId(APP_ID);
158 localNode = clusterService.getLocalNode().id();
159
160 componentConfigService.registerProperties(getClass());
161 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900162 deviceService.addListener(ovsdbListener);
163 deviceService.addListener(bridgeListener);
164 osNodeService.addListener(osNodeListener);
165
166 log.info("Started");
167 }
168
169 @Deactivate
170 protected void deactivate() {
171 osNodeService.removeListener(osNodeListener);
172 deviceService.removeListener(bridgeListener);
173 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900174 componentConfigService.unregisterProperties(getClass(), false);
175 leadershipService.withdraw(appId.name());
176 eventExecutor.shutdown();
177
178 log.info("Stopped");
179 }
180
181 @Modified
182 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900183 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900184
185 log.info("Modified");
186 }
187
188 @Override
189 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900190 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900191 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
192 return;
193 }
194 if (!deviceService.isAvailable(osNode.intgBridge())) {
195 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
196 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900197 }
198
199 @Override
200 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900201 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900202 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900203 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
204 return;
205 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900206
daniel parkb18424c2018-02-05 15:43:43 +0900207 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900208 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
209 osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900210 }
211
212 if (osNode.dataIp() != null &&
213 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
214 createTunnelInterface(osNode);
215 }
216
217 if (osNode.vlanIntf() != null &&
218 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Jian Li97482c12018-07-03 01:08:23 +0900219 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
220 osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900221 }
Jian Lie6312162018-03-21 21:41:00 +0900222
223 osNode.phyIntfs().forEach(i -> {
224 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900225 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
226 i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900227 }
228 });
229
daniel parkb18424c2018-02-05 15:43:43 +0900230 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900231 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900232 }
233 }
234
235 @Override
236 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900237 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900238 }
239
240 @Override
241 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900242 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900243 }
244
Jian Li340165f2018-02-27 10:38:17 +0900245 /**
Jian Li340165f2018-02-27 10:38:17 +0900246 * Creates a bridge with a given name on a given openstack node.
247 *
248 * @param osNode openstack node
249 * @param bridgeName bridge name
250 * @param deviceId device identifier
251 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900252 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
253 Device device = deviceService.getDevice(osNode.ovsdb());
254 if (device == null || !device.is(BridgeConfig.class)) {
255 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
256 return;
257 }
258
Jian Li789fadb2018-07-10 13:59:47 +0900259 List<ControllerInfo> controllers;
260
261 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
262 controllers = (List<ControllerInfo>) osNode.controllers();
263 } else {
264 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900265 .map(ControllerNode::ip)
266 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900267
Jian Li789fadb2018-07-10 13:59:47 +0900268 controllers = controllerIps.stream()
269 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
270 .collect(Collectors.toList());
271 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900272
273 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900274
Hyunsun Moon0d457362017-06-27 17:19:41 +0900275 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
276 .name(bridgeName)
277 .failMode(BridgeDescription.FailMode.SECURE)
278 .datapathId(dpid)
279 .disableInBand()
280 .controllers(controllers)
281 .build();
282
283 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
284 bridgeConfig.addBridge(bridgeDesc);
285 }
286
Jian Li340165f2018-02-27 10:38:17 +0900287 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900288 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900289 *
290 * @param osNode openstack node
291 * @param bridgeName bridge name
292 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900293 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900294 */
Jian Li97482c12018-07-03 01:08:23 +0900295 private void addOrRemoveSystemInterface(OpenstackNode osNode,
296 String bridgeName,
297 String intfName,
Daniel Parkc4d06402018-05-28 15:57:37 +0900298 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900299 Device device = deviceService.getDevice(osNode.ovsdb());
300 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900301 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900302 return;
303 }
304 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900305
306 if (addOrRemove) {
307 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
308 } else {
309 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
310 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900311 }
312
Jian Li340165f2018-02-27 10:38:17 +0900313 /**
314 * Creates a tunnel interface in a given openstack node.
315 *
316 * @param osNode openstack node
317 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900318 private void createTunnelInterface(OpenstackNode osNode) {
319 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
320 return;
321 }
322
323 Device device = deviceService.getDevice(osNode.ovsdb());
324 if (device == null || !device.is(InterfaceConfig.class)) {
325 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
326 return;
327 }
328
329 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
330 .deviceId(INTEGRATION_BRIDGE)
331 .ifaceName(DEFAULT_TUNNEL)
332 .type(TunnelDescription.Type.VXLAN)
333 .remote(TunnelEndPoints.flowTunnelEndpoint())
334 .key(TunnelKeys.flowTunnelKey())
335 .build();
336
337 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
338 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
339 }
340
Hyunsun Moon0d457362017-06-27 17:19:41 +0900341 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
342 Device device = deviceService.getDevice(deviceId);
343 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
344 log.error("The extension treatment is not supported");
345 return null;
346 }
347
Ray Milkey74e59132018-01-17 15:24:52 -0800348 if (device == null) {
349 return null;
350 }
351
Jian Li97482c12018-07-03 01:08:23 +0900352 ExtensionTreatmentResolver resolver =
353 device.as(ExtensionTreatmentResolver.class);
354 ExtensionTreatment treatment =
355 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900356 try {
357 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
358 return treatment;
359 } catch (ExtensionPropertyException e) {
360 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
361 return null;
362 }
363 }
364
Jian Li340165f2018-02-27 10:38:17 +0900365 /**
366 * Checks whether a given network interface in a given openstack node is enabled or not.
367 *
368 * @param osNode openstack node
369 * @param intf network interface name
370 * @return true if the given interface is enabled, false otherwise
371 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900372 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900373 return deviceService.isAvailable(osNode.intgBridge()) &&
374 deviceService.getPorts(osNode.intgBridge()).stream()
375 .anyMatch(port -> Objects.equals(
376 port.annotations().value(PORT_NAME), intf) &&
377 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900378 }
379
Jian Li340165f2018-02-27 10:38:17 +0900380 /**
381 * Checks whether all requirements for this state are fulfilled or not.
382 *
383 * @param osNode openstack node
384 * @return true if all requirements are fulfilled, false otherwise
385 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900386 private boolean isCurrentStateDone(OpenstackNode osNode) {
387 switch (osNode.state()) {
388 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900389 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
390 return false;
391 }
392
Jian Li5afbea42018-02-28 10:37:03 +0900393 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900394 case DEVICE_CREATED:
395 if (osNode.dataIp() != null &&
396 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
397 return false;
398 }
399 if (osNode.vlanIntf() != null &&
400 !isIntfEnabled(osNode, osNode.vlanIntf())) {
401 return false;
402 }
daniel parkb18424c2018-02-05 15:43:43 +0900403 if (osNode.type() == GATEWAY &&
404 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900405 return false;
406 }
Jian Lie6312162018-03-21 21:41:00 +0900407
408 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
409 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
410 return false;
411 }
412 }
413
Hyunsun Moon0d457362017-06-27 17:19:41 +0900414 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900415 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900416 case INCOMPLETE:
417 // always return false
418 // run init CLI to re-trigger node bootstrap
419 return false;
420 default:
421 return true;
422 }
423 }
424
Jian Li340165f2018-02-27 10:38:17 +0900425 /**
426 * Configures the openstack node with new state.
427 *
428 * @param osNode openstack node
429 * @param newState a new state
430 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900431 private void setState(OpenstackNode osNode, NodeState newState) {
432 if (osNode.state() == newState) {
433 return;
434 }
435 OpenstackNode updated = osNode.updateState(newState);
436 osNodeAdminService.updateNode(updated);
437 log.info("Changed {} state: {}", osNode.hostname(), newState);
438 }
439
Jian Li340165f2018-02-27 10:38:17 +0900440 /**
441 * Bootstraps a new openstack node.
442 *
443 * @param osNode openstack node
444 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900445 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900446 if (osNode.type() == CONTROLLER) {
447 if (osNode.state() == INIT && checkEndpoint(osNode)) {
448 setState(osNode, COMPLETE);
449 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900450 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900451 if (isCurrentStateDone(osNode)) {
452 setState(osNode, osNode.state().nextState());
453 } else {
Jian Li97482c12018-07-03 01:08:23 +0900454 log.trace("Processing {} state for {}", osNode.state(),
455 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900456 osNode.state().process(this, osNode);
457 }
458 }
459 }
460
461 /**
462 * Checks the validity of the given endpoint.
463 *
464 * @param osNode gateway node
465 * @return validity result
466 */
467 private boolean checkEndpoint(OpenstackNode osNode) {
468 if (osNode == null) {
469 log.warn("Keystone auth info has not been configured. " +
470 "Please specify auth info via network-cfg.json.");
471 return false;
472 }
473
474 OSClient client = getConnectedClient(osNode);
475
476 if (client == null) {
477 return false;
478 } else {
479 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900480 }
481 }
482
Jian Li340165f2018-02-27 10:38:17 +0900483 /**
Jian Li97482c12018-07-03 01:08:23 +0900484 * Extracts properties from the component configuration context.
485 *
486 * @param context the component context
487 */
488 private void readComponentConfiguration(ComponentContext context) {
489 Dictionary<?, ?> properties = context.getProperties();
490
491 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
492 if (ovsdbPortConfigured == null) {
493 ovsdbPort = DEFAULT_OVSDB_PORT;
494 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
495 } else {
496 ovsdbPort = ovsdbPortConfigured;
497 log.info("Configured. OVSDB port is {}", ovsdbPort);
498 }
499
500 Boolean autoRecoveryConfigured =
501 getBooleanProperty(properties, AUTO_RECOVERY);
502 if (autoRecoveryConfigured == null) {
503 autoRecovery = DEFAULT_AUTO_RECOVERY;
504 log.info("Auto recovery flag is NOT " +
505 "configured, default value is {}", autoRecovery);
506 } else {
507 autoRecovery = autoRecoveryConfigured;
508 log.info("Configured. Auto recovery flag is {}", autoRecovery);
509 }
510 }
511
512 /**
Jian Li340165f2018-02-27 10:38:17 +0900513 * An internal OVSDB listener. This listener is used for listening the
514 * network facing events from OVSDB device. If a new OVSDB device is detected,
515 * ONOS tries to bootstrap the openstack node.
516 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900517 private class InternalOvsdbListener implements DeviceListener {
518
519 @Override
520 public boolean isRelevant(DeviceEvent event) {
521 NodeId leader = leadershipService.getLeader(appId.name());
522 return Objects.equals(localNode, leader) &&
523 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900524 osNodeService.node(event.subject().id()) != null &&
525 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900526 }
527
528 @Override
529 public void event(DeviceEvent event) {
530 Device device = event.subject();
531 OpenstackNode osNode = osNodeService.node(device.id());
532
533 switch (event.type()) {
534 case DEVICE_AVAILABILITY_CHANGED:
535 case DEVICE_ADDED:
536 eventExecutor.execute(() -> {
537 if (deviceService.isAvailable(device.id())) {
538 log.debug("OVSDB {} detected", device.id());
539 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900540 }
541 });
542 break;
543 case PORT_ADDED:
544 case PORT_REMOVED:
545 case DEVICE_REMOVED:
546 default:
547 // do nothing
548 break;
549 }
550 }
551 }
552
Jian Li340165f2018-02-27 10:38:17 +0900553 /**
554 * An internal integration bridge listener. This listener is used for
555 * listening the events from integration bridge. To listen the events from
556 * other types of bridge such as provider bridge or tunnel bridge, we need
557 * to augment OpenstackNodeService.node() method.
558 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900559 private class InternalBridgeListener implements DeviceListener {
560
561 @Override
562 public boolean isRelevant(DeviceEvent event) {
563 NodeId leader = leadershipService.getLeader(appId.name());
564 return Objects.equals(localNode, leader) &&
565 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900566 osNodeService.node(event.subject().id()) != null &&
567 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900568 }
569
570 @Override
571 public void event(DeviceEvent event) {
572 Device device = event.subject();
573 OpenstackNode osNode = osNodeService.node(device.id());
574
575 switch (event.type()) {
576 case DEVICE_AVAILABILITY_CHANGED:
577 case DEVICE_ADDED:
578 eventExecutor.execute(() -> {
579 if (deviceService.isAvailable(device.id())) {
580 log.debug("Integration bridge created on {}", osNode.hostname());
581 bootstrapNode(osNode);
582 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900583 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900584 setState(osNode, INCOMPLETE);
585 }
Jian Li97482c12018-07-03 01:08:23 +0900586
587 if (autoRecovery) {
588 if (osNode.state() == INCOMPLETE ||
589 osNode.state() == DEVICE_CREATED) {
590 log.info("Device {} is reconnected", device.id());
591 osNodeAdminService.updateNode(
592 osNode.updateState(NodeState.INIT));
593 }
594 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900595 });
596 break;
597 case PORT_ADDED:
598 eventExecutor.execute(() -> {
599 Port port = event.port();
600 String portName = port.annotations().value(PORT_NAME);
601 if (osNode.state() == DEVICE_CREATED && (
602 Objects.equals(portName, DEFAULT_TUNNEL) ||
603 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900604 Objects.equals(portName, osNode.uplinkPort()) ||
605 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900606 log.debug("Interface {} added to {}",
607 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900608 bootstrapNode(osNode);
609 }
610 });
611 break;
612 case PORT_REMOVED:
613 eventExecutor.execute(() -> {
614 Port port = event.port();
615 String portName = port.annotations().value(PORT_NAME);
616 if (osNode.state() == COMPLETE && (
617 Objects.equals(portName, DEFAULT_TUNNEL) ||
618 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900619 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Lie6312162018-03-21 21:41:00 +0900620 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900621 log.warn("Interface {} removed from {}",
622 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900623 setState(osNode, INCOMPLETE);
624 }
625 });
626 break;
627 case PORT_UPDATED:
628 case DEVICE_REMOVED:
629 default:
630 // do nothing
631 break;
632 }
633 }
634 }
635
Jian Li340165f2018-02-27 10:38:17 +0900636 /**
Jian Lie6312162018-03-21 21:41:00 +0900637 * Checks whether the openstack node contains the given physical interface.
638 *
639 * @param osNode openstack node
640 * @param portName physical interface
641 * @return true if openstack node contains the given physical interface,
642 * false otherwise
643 */
644 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
645 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
646 if (Objects.equals(portName, phyIntf.intf())) {
647 return true;
648 }
649 }
650
651 return false;
652 }
653
654 /**
Jian Li340165f2018-02-27 10:38:17 +0900655 * An internal openstack node listener.
656 * The notification is triggered by OpenstackNodeStore.
657 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900658 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
659
660 @Override
661 public boolean isRelevant(OpenstackNodeEvent event) {
662 NodeId leader = leadershipService.getLeader(appId.name());
663 return Objects.equals(localNode, leader);
664 }
665
666 @Override
667 public void event(OpenstackNodeEvent event) {
668 switch (event.type()) {
669 case OPENSTACK_NODE_CREATED:
670 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900671 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900672 break;
673 case OPENSTACK_NODE_COMPLETE:
674 break;
675 case OPENSTACK_NODE_REMOVED:
676 break;
677 default:
678 break;
679 }
680 }
681 }
682}