blob: 433735590feb5c392bbafebf0ea2bf772ba556b1 [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;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090081import static org.onosproject.openstacknode.api.DpdkConfig.DatapathType.NETDEV;
Jian Li5afbea42018-02-28 10:37:03 +090082import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
83import static org.onosproject.openstacknode.api.NodeState.DEVICE_CREATED;
84import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
Jian Li51b844c2018-05-31 10:59:03 +090085import static org.onosproject.openstacknode.api.NodeState.INIT;
Daniel Parkd02d7bd2018-08-23 23:04:31 +090086
Jian Li51b844c2018-05-31 10:59:03 +090087import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090088import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
89import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Jian Li97482c12018-07-03 01:08:23 +090090import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090091import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090092import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090093import static org.slf4j.LoggerFactory.getLogger;
94
95/**
96 * Service bootstraps openstack node based on its type.
97 */
98@Component(immediate = true)
99public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
100
Jian Li5afbea42018-02-28 10:37:03 +0900101 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900102
103 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900104 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900105 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900106 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900107 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900108 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900109 private static final int DPID_BEGIN = 3;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected LeadershipService leadershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ClusterService clusterService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected DeviceService deviceService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected DeviceAdminService deviceAdminService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected OvsdbController ovsdbController;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900130 protected OpenstackNodeService osNodeService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected OpenstackNodeAdminService osNodeAdminService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected ComponentConfigService componentConfigService;
137
138 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
139 label = "OVSDB server listen port")
140 private int ovsdbPort = DEFAULT_OVSDB_PORT;
141
Jian Li97482c12018-07-03 01:08:23 +0900142 @Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
143 label = "A flag which indicates whether auto-recover openstack " +
144 "node status at the receiving of switch reconnecting event.")
145 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
146
Hyunsun Moon0d457362017-06-27 17:19:41 +0900147 private final ExecutorService eventExecutor = newSingleThreadExecutor(
148 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
149
150 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
151 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900152 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
153
154 private ApplicationId appId;
155 private NodeId localNode;
156
157 @Activate
158 protected void activate() {
159 appId = coreService.getAppId(APP_ID);
160 localNode = clusterService.getLocalNode().id();
161
162 componentConfigService.registerProperties(getClass());
163 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900164 deviceService.addListener(ovsdbListener);
165 deviceService.addListener(bridgeListener);
166 osNodeService.addListener(osNodeListener);
167
168 log.info("Started");
169 }
170
171 @Deactivate
172 protected void deactivate() {
173 osNodeService.removeListener(osNodeListener);
174 deviceService.removeListener(bridgeListener);
175 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900176 componentConfigService.unregisterProperties(getClass(), false);
177 leadershipService.withdraw(appId.name());
178 eventExecutor.shutdown();
179
180 log.info("Stopped");
181 }
182
183 @Modified
184 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900185 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900186
187 log.info("Modified");
188 }
189
190 @Override
191 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900192 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900193 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
194 return;
195 }
196 if (!deviceService.isAvailable(osNode.intgBridge())) {
197 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
198 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900199 }
200
201 @Override
202 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900203 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900204 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900205 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
206 return;
207 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900208
daniel parkb18424c2018-02-05 15:43:43 +0900209 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900210 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
211 osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900212 }
213
214 if (osNode.dataIp() != null &&
215 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
216 createTunnelInterface(osNode);
217 }
218
219 if (osNode.vlanIntf() != null &&
220 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Jian Li97482c12018-07-03 01:08:23 +0900221 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
222 osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900223 }
Jian Lie6312162018-03-21 21:41:00 +0900224
225 osNode.phyIntfs().forEach(i -> {
226 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900227 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
228 i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900229 }
230 });
231
daniel parkb18424c2018-02-05 15:43:43 +0900232 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900233 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900234 }
235 }
236
237 @Override
238 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900239 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900240 }
241
242 @Override
243 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900244 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900245 }
246
Jian Li340165f2018-02-27 10:38:17 +0900247 /**
Jian Li340165f2018-02-27 10:38:17 +0900248 * Creates a bridge with a given name on a given openstack node.
249 *
250 * @param osNode openstack node
251 * @param bridgeName bridge name
252 * @param deviceId device identifier
253 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900254 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
255 Device device = deviceService.getDevice(osNode.ovsdb());
256 if (device == null || !device.is(BridgeConfig.class)) {
257 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
258 return;
259 }
260
Jian Li789fadb2018-07-10 13:59:47 +0900261 List<ControllerInfo> controllers;
262
263 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
264 controllers = (List<ControllerInfo>) osNode.controllers();
265 } else {
266 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900267 .map(ControllerNode::ip)
268 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900269
Jian Li789fadb2018-07-10 13:59:47 +0900270 controllers = controllerIps.stream()
271 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
272 .collect(Collectors.toList());
273 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900274
275 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900276
Daniel Park92abf312018-08-08 17:01:35 +0900277 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900278 .name(bridgeName)
279 .failMode(BridgeDescription.FailMode.SECURE)
280 .datapathId(dpid)
281 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900282 .controllers(controllers);
283
284 if (osNode.datapathType().equals(NETDEV)) {
285 builder.datapathType(osNode.datapathType().name().toLowerCase());
286 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900287
288 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900289 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900290 }
291
Jian Li340165f2018-02-27 10:38:17 +0900292 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900293 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900294 *
295 * @param osNode openstack node
296 * @param bridgeName bridge name
297 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900298 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900299 */
Jian Li97482c12018-07-03 01:08:23 +0900300 private void addOrRemoveSystemInterface(OpenstackNode osNode,
301 String bridgeName,
302 String intfName,
Daniel Parkc4d06402018-05-28 15:57:37 +0900303 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900304 Device device = deviceService.getDevice(osNode.ovsdb());
305 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900306 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900307 return;
308 }
309 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900310
311 if (addOrRemove) {
312 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
313 } else {
314 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
315 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900316 }
317
Jian Li340165f2018-02-27 10:38:17 +0900318 /**
319 * Creates a tunnel interface in a given openstack node.
320 *
321 * @param osNode openstack node
322 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900323 private void createTunnelInterface(OpenstackNode osNode) {
324 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
325 return;
326 }
327
328 Device device = deviceService.getDevice(osNode.ovsdb());
329 if (device == null || !device.is(InterfaceConfig.class)) {
330 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
331 return;
332 }
333
334 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
335 .deviceId(INTEGRATION_BRIDGE)
336 .ifaceName(DEFAULT_TUNNEL)
337 .type(TunnelDescription.Type.VXLAN)
338 .remote(TunnelEndPoints.flowTunnelEndpoint())
339 .key(TunnelKeys.flowTunnelKey())
340 .build();
341
342 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
343 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
344 }
345
Hyunsun Moon0d457362017-06-27 17:19:41 +0900346 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
347 Device device = deviceService.getDevice(deviceId);
348 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
349 log.error("The extension treatment is not supported");
350 return null;
351 }
352
Ray Milkey74e59132018-01-17 15:24:52 -0800353 if (device == null) {
354 return null;
355 }
356
Jian Li97482c12018-07-03 01:08:23 +0900357 ExtensionTreatmentResolver resolver =
358 device.as(ExtensionTreatmentResolver.class);
359 ExtensionTreatment treatment =
360 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900361 try {
362 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
363 return treatment;
364 } catch (ExtensionPropertyException e) {
365 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
366 return null;
367 }
368 }
369
Jian Li340165f2018-02-27 10:38:17 +0900370 /**
371 * Checks whether a given network interface in a given openstack node is enabled or not.
372 *
373 * @param osNode openstack node
374 * @param intf network interface name
375 * @return true if the given interface is enabled, false otherwise
376 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900377 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900378 return deviceService.isAvailable(osNode.intgBridge()) &&
379 deviceService.getPorts(osNode.intgBridge()).stream()
380 .anyMatch(port -> Objects.equals(
381 port.annotations().value(PORT_NAME), intf) &&
382 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900383 }
384
Jian Li340165f2018-02-27 10:38:17 +0900385 /**
386 * Checks whether all requirements for this state are fulfilled or not.
387 *
388 * @param osNode openstack node
389 * @return true if all requirements are fulfilled, false otherwise
390 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 private boolean isCurrentStateDone(OpenstackNode osNode) {
392 switch (osNode.state()) {
393 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900394 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
395 return false;
396 }
397
Jian Li5afbea42018-02-28 10:37:03 +0900398 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900399 case DEVICE_CREATED:
400 if (osNode.dataIp() != null &&
401 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
402 return false;
403 }
404 if (osNode.vlanIntf() != null &&
405 !isIntfEnabled(osNode, osNode.vlanIntf())) {
406 return false;
407 }
daniel parkb18424c2018-02-05 15:43:43 +0900408 if (osNode.type() == GATEWAY &&
409 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900410 return false;
411 }
Jian Lie6312162018-03-21 21:41:00 +0900412
413 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
414 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
415 return false;
416 }
417 }
418
Hyunsun Moon0d457362017-06-27 17:19:41 +0900419 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900420 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900421 case INCOMPLETE:
422 // always return false
423 // run init CLI to re-trigger node bootstrap
424 return false;
425 default:
426 return true;
427 }
428 }
429
Jian Li340165f2018-02-27 10:38:17 +0900430 /**
431 * Configures the openstack node with new state.
432 *
433 * @param osNode openstack node
434 * @param newState a new state
435 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900436 private void setState(OpenstackNode osNode, NodeState newState) {
437 if (osNode.state() == newState) {
438 return;
439 }
440 OpenstackNode updated = osNode.updateState(newState);
441 osNodeAdminService.updateNode(updated);
442 log.info("Changed {} state: {}", osNode.hostname(), newState);
443 }
444
Jian Li340165f2018-02-27 10:38:17 +0900445 /**
446 * Bootstraps a new openstack node.
447 *
448 * @param osNode openstack node
449 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900450 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900451 if (osNode.type() == CONTROLLER) {
452 if (osNode.state() == INIT && checkEndpoint(osNode)) {
453 setState(osNode, COMPLETE);
454 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900455 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900456 if (isCurrentStateDone(osNode)) {
457 setState(osNode, osNode.state().nextState());
458 } else {
Jian Li97482c12018-07-03 01:08:23 +0900459 log.trace("Processing {} state for {}", osNode.state(),
460 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900461 osNode.state().process(this, osNode);
462 }
463 }
464 }
465
466 /**
467 * Checks the validity of the given endpoint.
468 *
469 * @param osNode gateway node
470 * @return validity result
471 */
472 private boolean checkEndpoint(OpenstackNode osNode) {
473 if (osNode == null) {
474 log.warn("Keystone auth info has not been configured. " +
475 "Please specify auth info via network-cfg.json.");
476 return false;
477 }
478
479 OSClient client = getConnectedClient(osNode);
480
481 if (client == null) {
482 return false;
483 } else {
484 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900485 }
486 }
487
Jian Li340165f2018-02-27 10:38:17 +0900488 /**
Jian Li97482c12018-07-03 01:08:23 +0900489 * Extracts properties from the component configuration context.
490 *
491 * @param context the component context
492 */
493 private void readComponentConfiguration(ComponentContext context) {
494 Dictionary<?, ?> properties = context.getProperties();
495
496 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
497 if (ovsdbPortConfigured == null) {
498 ovsdbPort = DEFAULT_OVSDB_PORT;
499 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
500 } else {
501 ovsdbPort = ovsdbPortConfigured;
502 log.info("Configured. OVSDB port is {}", ovsdbPort);
503 }
504
505 Boolean autoRecoveryConfigured =
506 getBooleanProperty(properties, AUTO_RECOVERY);
507 if (autoRecoveryConfigured == null) {
508 autoRecovery = DEFAULT_AUTO_RECOVERY;
509 log.info("Auto recovery flag is NOT " +
510 "configured, default value is {}", autoRecovery);
511 } else {
512 autoRecovery = autoRecoveryConfigured;
513 log.info("Configured. Auto recovery flag is {}", autoRecovery);
514 }
515 }
516
517 /**
Jian Li340165f2018-02-27 10:38:17 +0900518 * An internal OVSDB listener. This listener is used for listening the
519 * network facing events from OVSDB device. If a new OVSDB device is detected,
520 * ONOS tries to bootstrap the openstack node.
521 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900522 private class InternalOvsdbListener implements DeviceListener {
523
524 @Override
525 public boolean isRelevant(DeviceEvent event) {
526 NodeId leader = leadershipService.getLeader(appId.name());
527 return Objects.equals(localNode, leader) &&
528 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900529 osNodeService.node(event.subject().id()) != null &&
530 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900531 }
532
533 @Override
534 public void event(DeviceEvent event) {
535 Device device = event.subject();
536 OpenstackNode osNode = osNodeService.node(device.id());
537
538 switch (event.type()) {
539 case DEVICE_AVAILABILITY_CHANGED:
540 case DEVICE_ADDED:
541 eventExecutor.execute(() -> {
542 if (deviceService.isAvailable(device.id())) {
543 log.debug("OVSDB {} detected", device.id());
544 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900545 }
546 });
547 break;
548 case PORT_ADDED:
549 case PORT_REMOVED:
550 case DEVICE_REMOVED:
551 default:
552 // do nothing
553 break;
554 }
555 }
556 }
557
Jian Li340165f2018-02-27 10:38:17 +0900558 /**
559 * An internal integration bridge listener. This listener is used for
560 * listening the events from integration bridge. To listen the events from
561 * other types of bridge such as provider bridge or tunnel bridge, we need
562 * to augment OpenstackNodeService.node() method.
563 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900564 private class InternalBridgeListener implements DeviceListener {
565
566 @Override
567 public boolean isRelevant(DeviceEvent event) {
568 NodeId leader = leadershipService.getLeader(appId.name());
569 return Objects.equals(localNode, leader) &&
570 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900571 osNodeService.node(event.subject().id()) != null &&
572 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900573 }
574
575 @Override
576 public void event(DeviceEvent event) {
577 Device device = event.subject();
578 OpenstackNode osNode = osNodeService.node(device.id());
579
580 switch (event.type()) {
581 case DEVICE_AVAILABILITY_CHANGED:
582 case DEVICE_ADDED:
583 eventExecutor.execute(() -> {
584 if (deviceService.isAvailable(device.id())) {
585 log.debug("Integration bridge created on {}", osNode.hostname());
586 bootstrapNode(osNode);
587 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900588 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900589 setState(osNode, INCOMPLETE);
590 }
Jian Li97482c12018-07-03 01:08:23 +0900591
592 if (autoRecovery) {
593 if (osNode.state() == INCOMPLETE ||
594 osNode.state() == DEVICE_CREATED) {
595 log.info("Device {} is reconnected", device.id());
596 osNodeAdminService.updateNode(
597 osNode.updateState(NodeState.INIT));
598 }
599 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900600 });
601 break;
602 case PORT_ADDED:
603 eventExecutor.execute(() -> {
604 Port port = event.port();
605 String portName = port.annotations().value(PORT_NAME);
606 if (osNode.state() == DEVICE_CREATED && (
607 Objects.equals(portName, DEFAULT_TUNNEL) ||
608 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900609 Objects.equals(portName, osNode.uplinkPort()) ||
610 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900611 log.debug("Interface {} added to {}",
612 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900613 bootstrapNode(osNode);
614 }
615 });
616 break;
617 case PORT_REMOVED:
618 eventExecutor.execute(() -> {
619 Port port = event.port();
620 String portName = port.annotations().value(PORT_NAME);
621 if (osNode.state() == COMPLETE && (
622 Objects.equals(portName, DEFAULT_TUNNEL) ||
623 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900624 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Lie6312162018-03-21 21:41:00 +0900625 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900626 log.warn("Interface {} removed from {}",
627 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900628 setState(osNode, INCOMPLETE);
629 }
630 });
631 break;
632 case PORT_UPDATED:
633 case DEVICE_REMOVED:
634 default:
635 // do nothing
636 break;
637 }
638 }
639 }
640
Jian Li340165f2018-02-27 10:38:17 +0900641 /**
Jian Lie6312162018-03-21 21:41:00 +0900642 * Checks whether the openstack node contains the given physical interface.
643 *
644 * @param osNode openstack node
645 * @param portName physical interface
646 * @return true if openstack node contains the given physical interface,
647 * false otherwise
648 */
649 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
650 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
651 if (Objects.equals(portName, phyIntf.intf())) {
652 return true;
653 }
654 }
655
656 return false;
657 }
658
659 /**
Jian Li340165f2018-02-27 10:38:17 +0900660 * An internal openstack node listener.
661 * The notification is triggered by OpenstackNodeStore.
662 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900663 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
664
665 @Override
666 public boolean isRelevant(OpenstackNodeEvent event) {
667 NodeId leader = leadershipService.getLeader(appId.name());
668 return Objects.equals(localNode, leader);
669 }
670
671 @Override
672 public void event(OpenstackNodeEvent event) {
673 switch (event.type()) {
674 case OPENSTACK_NODE_CREATED:
675 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900676 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900677 break;
678 case OPENSTACK_NODE_COMPLETE:
679 break;
680 case OPENSTACK_NODE_REMOVED:
681 break;
682 default:
683 break;
684 }
685 }
686 }
687}