blob: 07d71de79d6e2a575e5f6e10ac286c99725da097 [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;
Daniel Park92abf312018-08-08 17:01:35 +090085import static org.onosproject.openstacknode.api.OpenstackNode.DatapathType.NETDEV;
Jian Li51b844c2018-05-31 10:59:03 +090086import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +090087import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
88import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
Jian Li97482c12018-07-03 01:08:23 +090089import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getBooleanProperty;
Jian Li51b844c2018-05-31 10:59:03 +090090import static org.onosproject.openstacknode.util.OpenstackNodeUtil.getConnectedClient;
Daniel Parkc4d06402018-05-28 15:57:37 +090091import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090092import static org.slf4j.LoggerFactory.getLogger;
93
94/**
95 * Service bootstraps openstack node based on its type.
96 */
97@Component(immediate = true)
98public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
99
Jian Li5afbea42018-02-28 10:37:03 +0900100 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900101
102 private static final String OVSDB_PORT = "ovsdbPortNum";
Jian Li97482c12018-07-03 01:08:23 +0900103 private static final String AUTO_RECOVERY = "autoRecovery";
Hyunsun Moon0d457362017-06-27 17:19:41 +0900104 private static final String DEFAULT_OF_PROTO = "tcp";
Daniel Parkc4d06402018-05-28 15:57:37 +0900105 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900106 private static final int DEFAULT_OFPORT = 6653;
Jian Li97482c12018-07-03 01:08:23 +0900107 private static final boolean DEFAULT_AUTO_RECOVERY = true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900108 private static final int DPID_BEGIN = 3;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected CoreService coreService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected LeadershipService leadershipService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected ClusterService clusterService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected DeviceService deviceService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected DeviceAdminService deviceAdminService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected OvsdbController ovsdbController;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 protected OpenstackNodeService osNodeService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected OpenstackNodeAdminService osNodeAdminService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected ComponentConfigService componentConfigService;
136
137 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
138 label = "OVSDB server listen port")
139 private int ovsdbPort = DEFAULT_OVSDB_PORT;
140
Jian Li97482c12018-07-03 01:08:23 +0900141 @Property(name = AUTO_RECOVERY, boolValue = DEFAULT_AUTO_RECOVERY,
142 label = "A flag which indicates whether auto-recover openstack " +
143 "node status at the receiving of switch reconnecting event.")
144 private boolean autoRecovery = DEFAULT_AUTO_RECOVERY;
145
Hyunsun Moon0d457362017-06-27 17:19:41 +0900146 private final ExecutorService eventExecutor = newSingleThreadExecutor(
147 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
148
149 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
150 private final DeviceListener bridgeListener = new InternalBridgeListener();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900151 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
152
153 private ApplicationId appId;
154 private NodeId localNode;
155
156 @Activate
157 protected void activate() {
158 appId = coreService.getAppId(APP_ID);
159 localNode = clusterService.getLocalNode().id();
160
161 componentConfigService.registerProperties(getClass());
162 leadershipService.runForLeadership(appId.name());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900163 deviceService.addListener(ovsdbListener);
164 deviceService.addListener(bridgeListener);
165 osNodeService.addListener(osNodeListener);
166
167 log.info("Started");
168 }
169
170 @Deactivate
171 protected void deactivate() {
172 osNodeService.removeListener(osNodeListener);
173 deviceService.removeListener(bridgeListener);
174 deviceService.removeListener(ovsdbListener);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900175 componentConfigService.unregisterProperties(getClass(), false);
176 leadershipService.withdraw(appId.name());
177 eventExecutor.shutdown();
178
179 log.info("Stopped");
180 }
181
182 @Modified
183 protected void modified(ComponentContext context) {
Jian Li97482c12018-07-03 01:08:23 +0900184 readComponentConfiguration(context);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900185
186 log.info("Modified");
187 }
188
189 @Override
190 public void processInitState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900191 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900192 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
193 return;
194 }
195 if (!deviceService.isAvailable(osNode.intgBridge())) {
196 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
197 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900198 }
199
200 @Override
201 public void processDeviceCreatedState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900202 try {
Daniel Parkc4d06402018-05-28 15:57:37 +0900203 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
daniel parkb18424c2018-02-05 15:43:43 +0900204 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
205 return;
206 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900207
daniel parkb18424c2018-02-05 15:43:43 +0900208 if (osNode.type() == GATEWAY) {
Jian Li97482c12018-07-03 01:08:23 +0900209 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
210 osNode.uplinkPort(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900211 }
212
213 if (osNode.dataIp() != null &&
214 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
215 createTunnelInterface(osNode);
216 }
217
218 if (osNode.vlanIntf() != null &&
219 !isIntfEnabled(osNode, osNode.vlanIntf())) {
Jian Li97482c12018-07-03 01:08:23 +0900220 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
221 osNode.vlanIntf(), true);
daniel parkb18424c2018-02-05 15:43:43 +0900222 }
Jian Lie6312162018-03-21 21:41:00 +0900223
224 osNode.phyIntfs().forEach(i -> {
225 if (!isIntfEnabled(osNode, i.intf())) {
Jian Li97482c12018-07-03 01:08:23 +0900226 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE,
227 i.intf(), true);
Jian Lie6312162018-03-21 21:41:00 +0900228 }
229 });
230
daniel parkb18424c2018-02-05 15:43:43 +0900231 } catch (Exception e) {
Jian Li340165f2018-02-27 10:38:17 +0900232 log.error("Exception occurred because of {}", e.toString());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900233 }
234 }
235
236 @Override
237 public void processCompleteState(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900238 //Do something if needed
Hyunsun Moon0d457362017-06-27 17:19:41 +0900239 }
240
241 @Override
242 public void processIncompleteState(OpenstackNode osNode) {
daniel parkb18424c2018-02-05 15:43:43 +0900243 //TODO
Hyunsun Moon0d457362017-06-27 17:19:41 +0900244 }
245
Jian Li340165f2018-02-27 10:38:17 +0900246 /**
Jian Li340165f2018-02-27 10:38:17 +0900247 * Creates a bridge with a given name on a given openstack node.
248 *
249 * @param osNode openstack node
250 * @param bridgeName bridge name
251 * @param deviceId device identifier
252 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900253 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
254 Device device = deviceService.getDevice(osNode.ovsdb());
255 if (device == null || !device.is(BridgeConfig.class)) {
256 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
257 return;
258 }
259
Jian Li789fadb2018-07-10 13:59:47 +0900260 List<ControllerInfo> controllers;
261
262 if (osNode.controllers() != null && osNode.controllers().size() > 0) {
263 controllers = (List<ControllerInfo>) osNode.controllers();
264 } else {
265 Set<IpAddress> controllerIps = clusterService.getNodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900266 .map(ControllerNode::ip)
267 .collect(Collectors.toSet());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900268
Jian Li789fadb2018-07-10 13:59:47 +0900269 controllers = controllerIps.stream()
270 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
271 .collect(Collectors.toList());
272 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900273
274 String dpid = deviceId.toString().substring(DPID_BEGIN);
daniel parkb18424c2018-02-05 15:43:43 +0900275
Daniel Park92abf312018-08-08 17:01:35 +0900276 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900277 .name(bridgeName)
278 .failMode(BridgeDescription.FailMode.SECURE)
279 .datapathId(dpid)
280 .disableInBand()
Daniel Park92abf312018-08-08 17:01:35 +0900281 .controllers(controllers);
282
283 if (osNode.datapathType().equals(NETDEV)) {
284 builder.datapathType(osNode.datapathType().name().toLowerCase());
285 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900286
287 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Park92abf312018-08-08 17:01:35 +0900288 bridgeConfig.addBridge(builder.build());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900289 }
290
Jian Li340165f2018-02-27 10:38:17 +0900291 /**
Daniel Parkc4d06402018-05-28 15:57:37 +0900292 * Adds or removes a network interface (aka port) into a given bridge of openstack node.
Jian Li340165f2018-02-27 10:38:17 +0900293 *
294 * @param osNode openstack node
295 * @param bridgeName bridge name
296 * @param intfName interface name
Daniel Parkc4d06402018-05-28 15:57:37 +0900297 * @param addOrRemove add port is true, remove it otherwise
Jian Li340165f2018-02-27 10:38:17 +0900298 */
Jian Li97482c12018-07-03 01:08:23 +0900299 private void addOrRemoveSystemInterface(OpenstackNode osNode,
300 String bridgeName,
301 String intfName,
Daniel Parkc4d06402018-05-28 15:57:37 +0900302 boolean addOrRemove) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900303 Device device = deviceService.getDevice(osNode.ovsdb());
304 if (device == null || !device.is(BridgeConfig.class)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900305 log.info("device is null or this device if not ovsdb device");
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306 return;
307 }
308 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Daniel Parkc4d06402018-05-28 15:57:37 +0900309
310 if (addOrRemove) {
311 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
312 } else {
313 bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), intfName);
314 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900315 }
316
Jian Li340165f2018-02-27 10:38:17 +0900317 /**
318 * Creates a tunnel interface in a given openstack node.
319 *
320 * @param osNode openstack node
321 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900322 private void createTunnelInterface(OpenstackNode osNode) {
323 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
324 return;
325 }
326
327 Device device = deviceService.getDevice(osNode.ovsdb());
328 if (device == null || !device.is(InterfaceConfig.class)) {
329 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
330 return;
331 }
332
333 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
334 .deviceId(INTEGRATION_BRIDGE)
335 .ifaceName(DEFAULT_TUNNEL)
336 .type(TunnelDescription.Type.VXLAN)
337 .remote(TunnelEndPoints.flowTunnelEndpoint())
338 .key(TunnelKeys.flowTunnelKey())
339 .build();
340
341 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
342 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
343 }
344
Hyunsun Moon0d457362017-06-27 17:19:41 +0900345 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
346 Device device = deviceService.getDevice(deviceId);
347 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
348 log.error("The extension treatment is not supported");
349 return null;
350 }
351
Ray Milkey74e59132018-01-17 15:24:52 -0800352 if (device == null) {
353 return null;
354 }
355
Jian Li97482c12018-07-03 01:08:23 +0900356 ExtensionTreatmentResolver resolver =
357 device.as(ExtensionTreatmentResolver.class);
358 ExtensionTreatment treatment =
359 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900360 try {
361 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
362 return treatment;
363 } catch (ExtensionPropertyException e) {
364 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
365 return null;
366 }
367 }
368
Jian Li340165f2018-02-27 10:38:17 +0900369 /**
370 * Checks whether a given network interface in a given openstack node is enabled or not.
371 *
372 * @param osNode openstack node
373 * @param intf network interface name
374 * @return true if the given interface is enabled, false otherwise
375 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900376 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
Jian Li5afbea42018-02-28 10:37:03 +0900377 return deviceService.isAvailable(osNode.intgBridge()) &&
378 deviceService.getPorts(osNode.intgBridge()).stream()
379 .anyMatch(port -> Objects.equals(
380 port.annotations().value(PORT_NAME), intf) &&
381 port.isEnabled());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900382 }
383
Jian Li340165f2018-02-27 10:38:17 +0900384 /**
385 * Checks whether all requirements for this state are fulfilled or not.
386 *
387 * @param osNode openstack node
388 * @return true if all requirements are fulfilled, false otherwise
389 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900390 private boolean isCurrentStateDone(OpenstackNode osNode) {
391 switch (osNode.state()) {
392 case INIT:
Daniel Parkc4d06402018-05-28 15:57:37 +0900393 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
394 return false;
395 }
396
Jian Li5afbea42018-02-28 10:37:03 +0900397 return deviceService.isAvailable(osNode.intgBridge());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900398 case DEVICE_CREATED:
399 if (osNode.dataIp() != null &&
400 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
401 return false;
402 }
403 if (osNode.vlanIntf() != null &&
404 !isIntfEnabled(osNode, osNode.vlanIntf())) {
405 return false;
406 }
daniel parkb18424c2018-02-05 15:43:43 +0900407 if (osNode.type() == GATEWAY &&
408 !isIntfEnabled(osNode, osNode.uplinkPort())) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900409 return false;
410 }
Jian Lie6312162018-03-21 21:41:00 +0900411
412 for (OpenstackPhyInterface intf : osNode.phyIntfs()) {
413 if (intf != null && !isIntfEnabled(osNode, intf.intf())) {
414 return false;
415 }
416 }
417
Hyunsun Moon0d457362017-06-27 17:19:41 +0900418 return true;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900419 case COMPLETE:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900420 case INCOMPLETE:
421 // always return false
422 // run init CLI to re-trigger node bootstrap
423 return false;
424 default:
425 return true;
426 }
427 }
428
Jian Li340165f2018-02-27 10:38:17 +0900429 /**
430 * Configures the openstack node with new state.
431 *
432 * @param osNode openstack node
433 * @param newState a new state
434 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900435 private void setState(OpenstackNode osNode, NodeState newState) {
436 if (osNode.state() == newState) {
437 return;
438 }
439 OpenstackNode updated = osNode.updateState(newState);
440 osNodeAdminService.updateNode(updated);
441 log.info("Changed {} state: {}", osNode.hostname(), newState);
442 }
443
Jian Li340165f2018-02-27 10:38:17 +0900444 /**
445 * Bootstraps a new openstack node.
446 *
447 * @param osNode openstack node
448 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900449 private void bootstrapNode(OpenstackNode osNode) {
Jian Li51b844c2018-05-31 10:59:03 +0900450 if (osNode.type() == CONTROLLER) {
451 if (osNode.state() == INIT && checkEndpoint(osNode)) {
452 setState(osNode, COMPLETE);
453 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900454 } else {
Jian Li51b844c2018-05-31 10:59:03 +0900455 if (isCurrentStateDone(osNode)) {
456 setState(osNode, osNode.state().nextState());
457 } else {
Jian Li97482c12018-07-03 01:08:23 +0900458 log.trace("Processing {} state for {}", osNode.state(),
459 osNode.hostname());
Jian Li51b844c2018-05-31 10:59:03 +0900460 osNode.state().process(this, osNode);
461 }
462 }
463 }
464
465 /**
466 * Checks the validity of the given endpoint.
467 *
468 * @param osNode gateway node
469 * @return validity result
470 */
471 private boolean checkEndpoint(OpenstackNode osNode) {
472 if (osNode == null) {
473 log.warn("Keystone auth info has not been configured. " +
474 "Please specify auth info via network-cfg.json.");
475 return false;
476 }
477
478 OSClient client = getConnectedClient(osNode);
479
480 if (client == null) {
481 return false;
482 } else {
483 return client.getSupportedServices().size() != 0;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900484 }
485 }
486
Jian Li340165f2018-02-27 10:38:17 +0900487 /**
Jian Li97482c12018-07-03 01:08:23 +0900488 * Extracts properties from the component configuration context.
489 *
490 * @param context the component context
491 */
492 private void readComponentConfiguration(ComponentContext context) {
493 Dictionary<?, ?> properties = context.getProperties();
494
495 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
496 if (ovsdbPortConfigured == null) {
497 ovsdbPort = DEFAULT_OVSDB_PORT;
498 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPort);
499 } else {
500 ovsdbPort = ovsdbPortConfigured;
501 log.info("Configured. OVSDB port is {}", ovsdbPort);
502 }
503
504 Boolean autoRecoveryConfigured =
505 getBooleanProperty(properties, AUTO_RECOVERY);
506 if (autoRecoveryConfigured == null) {
507 autoRecovery = DEFAULT_AUTO_RECOVERY;
508 log.info("Auto recovery flag is NOT " +
509 "configured, default value is {}", autoRecovery);
510 } else {
511 autoRecovery = autoRecoveryConfigured;
512 log.info("Configured. Auto recovery flag is {}", autoRecovery);
513 }
514 }
515
516 /**
Jian Li340165f2018-02-27 10:38:17 +0900517 * An internal OVSDB listener. This listener is used for listening the
518 * network facing events from OVSDB device. If a new OVSDB device is detected,
519 * ONOS tries to bootstrap the openstack node.
520 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900521 private class InternalOvsdbListener implements DeviceListener {
522
523 @Override
524 public boolean isRelevant(DeviceEvent event) {
525 NodeId leader = leadershipService.getLeader(appId.name());
526 return Objects.equals(localNode, leader) &&
527 event.subject().type() == Device.Type.CONTROLLER &&
Jian Li51b844c2018-05-31 10:59:03 +0900528 osNodeService.node(event.subject().id()) != null &&
529 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900530 }
531
532 @Override
533 public void event(DeviceEvent event) {
534 Device device = event.subject();
535 OpenstackNode osNode = osNodeService.node(device.id());
536
537 switch (event.type()) {
538 case DEVICE_AVAILABILITY_CHANGED:
539 case DEVICE_ADDED:
540 eventExecutor.execute(() -> {
541 if (deviceService.isAvailable(device.id())) {
542 log.debug("OVSDB {} detected", device.id());
543 bootstrapNode(osNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900544 }
545 });
546 break;
547 case PORT_ADDED:
548 case PORT_REMOVED:
549 case DEVICE_REMOVED:
550 default:
551 // do nothing
552 break;
553 }
554 }
555 }
556
Jian Li340165f2018-02-27 10:38:17 +0900557 /**
558 * An internal integration bridge listener. This listener is used for
559 * listening the events from integration bridge. To listen the events from
560 * other types of bridge such as provider bridge or tunnel bridge, we need
561 * to augment OpenstackNodeService.node() method.
562 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900563 private class InternalBridgeListener implements DeviceListener {
564
565 @Override
566 public boolean isRelevant(DeviceEvent event) {
567 NodeId leader = leadershipService.getLeader(appId.name());
568 return Objects.equals(localNode, leader) &&
569 event.subject().type() == Device.Type.SWITCH &&
Jian Li51b844c2018-05-31 10:59:03 +0900570 osNodeService.node(event.subject().id()) != null &&
571 osNodeService.node(event.subject().id()).type() != CONTROLLER;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900572 }
573
574 @Override
575 public void event(DeviceEvent event) {
576 Device device = event.subject();
577 OpenstackNode osNode = osNodeService.node(device.id());
578
579 switch (event.type()) {
580 case DEVICE_AVAILABILITY_CHANGED:
581 case DEVICE_ADDED:
582 eventExecutor.execute(() -> {
583 if (deviceService.isAvailable(device.id())) {
584 log.debug("Integration bridge created on {}", osNode.hostname());
585 bootstrapNode(osNode);
586 } else if (osNode.state() == COMPLETE) {
Jian Li97482c12018-07-03 01:08:23 +0900587 log.info("Device {} disconnected", device.id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900588 setState(osNode, INCOMPLETE);
589 }
Jian Li97482c12018-07-03 01:08:23 +0900590
591 if (autoRecovery) {
592 if (osNode.state() == INCOMPLETE ||
593 osNode.state() == DEVICE_CREATED) {
594 log.info("Device {} is reconnected", device.id());
595 osNodeAdminService.updateNode(
596 osNode.updateState(NodeState.INIT));
597 }
598 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900599 });
600 break;
601 case PORT_ADDED:
602 eventExecutor.execute(() -> {
603 Port port = event.port();
604 String portName = port.annotations().value(PORT_NAME);
605 if (osNode.state() == DEVICE_CREATED && (
606 Objects.equals(portName, DEFAULT_TUNNEL) ||
607 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Lie6312162018-03-21 21:41:00 +0900608 Objects.equals(portName, osNode.uplinkPort()) ||
609 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900610 log.debug("Interface {} added to {}",
611 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900612 bootstrapNode(osNode);
613 }
614 });
615 break;
616 case PORT_REMOVED:
617 eventExecutor.execute(() -> {
618 Port port = event.port();
619 String portName = port.annotations().value(PORT_NAME);
620 if (osNode.state() == COMPLETE && (
621 Objects.equals(portName, DEFAULT_TUNNEL) ||
622 Objects.equals(portName, osNode.vlanIntf()) ||
Jian Li97482c12018-07-03 01:08:23 +0900623 Objects.equals(portName, osNode.uplinkPort()) ||
Jian Lie6312162018-03-21 21:41:00 +0900624 containsPhyIntf(osNode, portName))) {
Jian Li97482c12018-07-03 01:08:23 +0900625 log.warn("Interface {} removed from {}",
626 portName, event.subject().id());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900627 setState(osNode, INCOMPLETE);
628 }
629 });
630 break;
631 case PORT_UPDATED:
632 case DEVICE_REMOVED:
633 default:
634 // do nothing
635 break;
636 }
637 }
638 }
639
Jian Li340165f2018-02-27 10:38:17 +0900640 /**
Jian Lie6312162018-03-21 21:41:00 +0900641 * Checks whether the openstack node contains the given physical interface.
642 *
643 * @param osNode openstack node
644 * @param portName physical interface
645 * @return true if openstack node contains the given physical interface,
646 * false otherwise
647 */
648 private boolean containsPhyIntf(OpenstackNode osNode, String portName) {
649 for (OpenstackPhyInterface phyIntf : osNode.phyIntfs()) {
650 if (Objects.equals(portName, phyIntf.intf())) {
651 return true;
652 }
653 }
654
655 return false;
656 }
657
658 /**
Jian Li340165f2018-02-27 10:38:17 +0900659 * An internal openstack node listener.
660 * The notification is triggered by OpenstackNodeStore.
661 */
Hyunsun Moon0d457362017-06-27 17:19:41 +0900662 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
663
664 @Override
665 public boolean isRelevant(OpenstackNodeEvent event) {
666 NodeId leader = leadershipService.getLeader(appId.name());
667 return Objects.equals(localNode, leader);
668 }
669
670 @Override
671 public void event(OpenstackNodeEvent event) {
672 switch (event.type()) {
673 case OPENSTACK_NODE_CREATED:
674 case OPENSTACK_NODE_UPDATED:
Jian Li5afbea42018-02-28 10:37:03 +0900675 eventExecutor.execute(() -> bootstrapNode(event.subject()));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900676 break;
677 case OPENSTACK_NODE_COMPLETE:
678 break;
679 case OPENSTACK_NODE_REMOVED:
680 break;
681 default:
682 break;
683 }
684 }
685 }
686}