blob: c0d2c3cd7b6108450baa7b537b9473262562344f [file] [log] [blame]
Hyunsun Moon0d457362017-06-27 17:19:41 +09001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
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
18import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Sets;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.onlab.packet.IpAddress;
29import org.onlab.util.Tools;
30import org.onosproject.cfg.ComponentConfigService;
31import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.ControllerNode;
33import org.onosproject.cluster.LeadershipService;
34import org.onosproject.cluster.NodeId;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
37import org.onosproject.core.GroupId;
38import org.onosproject.net.Device;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.Port;
41import org.onosproject.net.behaviour.BridgeConfig;
42import org.onosproject.net.behaviour.BridgeDescription;
43import org.onosproject.net.behaviour.BridgeName;
44import org.onosproject.net.behaviour.ControllerInfo;
45import org.onosproject.net.behaviour.DefaultBridgeDescription;
46import org.onosproject.net.behaviour.DefaultPatchDescription;
47import org.onosproject.net.behaviour.DefaultTunnelDescription;
48import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
49import org.onosproject.net.behaviour.InterfaceConfig;
50import org.onosproject.net.behaviour.PatchDescription;
51import org.onosproject.net.behaviour.TunnelDescription;
52import org.onosproject.net.behaviour.TunnelEndPoints;
53import org.onosproject.net.behaviour.TunnelKeys;
54import org.onosproject.net.device.DeviceAdminService;
55import org.onosproject.net.device.DeviceEvent;
56import org.onosproject.net.device.DeviceListener;
57import org.onosproject.net.device.DeviceService;
58import org.onosproject.net.flow.DefaultTrafficTreatment;
59import org.onosproject.net.flow.TrafficTreatment;
60import org.onosproject.net.flow.instructions.ExtensionPropertyException;
61import org.onosproject.net.flow.instructions.ExtensionTreatment;
62import org.onosproject.net.group.DefaultGroupDescription;
63import org.onosproject.net.group.Group;
64import org.onosproject.net.group.GroupBucket;
65import org.onosproject.net.group.GroupBuckets;
66import org.onosproject.net.group.GroupDescription;
67import org.onosproject.net.group.GroupEvent;
68import org.onosproject.net.group.GroupListener;
69import org.onosproject.net.group.GroupService;
70import org.onosproject.openstacknode.api.NodeState;
71import org.onosproject.openstacknode.api.OpenstackNode;
72import org.onosproject.openstacknode.api.OpenstackNode.NetworkMode;
73import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
74import org.onosproject.openstacknode.api.OpenstackNodeEvent;
75import org.onosproject.openstacknode.api.OpenstackNodeHandler;
76import org.onosproject.openstacknode.api.OpenstackNodeListener;
77import org.onosproject.openstacknode.api.OpenstackNodeService;
78import org.onosproject.ovsdb.controller.OvsdbClientService;
79import org.onosproject.ovsdb.controller.OvsdbController;
80import org.onosproject.ovsdb.controller.OvsdbNodeId;
81import org.osgi.service.component.ComponentContext;
82import org.slf4j.Logger;
83
84import java.util.Dictionary;
85import java.util.List;
86import java.util.Objects;
87import java.util.Set;
88import java.util.concurrent.ExecutorService;
89import java.util.stream.Collectors;
90
91import static com.google.common.base.Preconditions.checkArgument;
92import static java.util.concurrent.Executors.newSingleThreadExecutor;
93import static org.onlab.packet.TpPort.tpPort;
94import static org.onlab.util.Tools.groupedThreads;
95import static org.onosproject.net.AnnotationKeys.PORT_NAME;
96import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
97import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
98import static org.onosproject.openstacknode.api.Constants.*;
99import static org.onosproject.openstacknode.api.Constants.PATCH_INTG_BRIDGE;
100import static org.onosproject.openstacknode.api.NodeState.*;
101import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VLAN;
102import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VXLAN;
103import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
104import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
105import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
106import static org.slf4j.LoggerFactory.getLogger;
107
108/**
109 * Service bootstraps openstack node based on its type.
110 */
111@Component(immediate = true)
112public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
113
114 protected final Logger log = getLogger(getClass());
115
116 private static final String OVSDB_PORT = "ovsdbPortNum";
117 private static final int DEFAULT_OVSDB_PORT = 6640;
118 private static final String DEFAULT_OF_PROTO = "tcp";
119 private static final int DEFAULT_OFPORT = 6653;
120 private static final int DPID_BEGIN = 3;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected CoreService coreService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected LeadershipService leadershipService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected ClusterService clusterService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected DeviceService deviceService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected DeviceAdminService deviceAdminService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected OvsdbController ovsdbController;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected GroupService groupService;
142
143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected OpenstackNodeService osNodeService;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
147 protected OpenstackNodeAdminService osNodeAdminService;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
150 protected ComponentConfigService componentConfigService;
151
152 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
153 label = "OVSDB server listen port")
154 private int ovsdbPort = DEFAULT_OVSDB_PORT;
155
156 private final ExecutorService eventExecutor = newSingleThreadExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
158
159 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
160 private final DeviceListener bridgeListener = new InternalBridgeListener();
161 private final GroupListener groupListener = new InternalGroupListener();
162 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
163
164 private ApplicationId appId;
165 private NodeId localNode;
166
167 @Activate
168 protected void activate() {
169 appId = coreService.getAppId(APP_ID);
170 localNode = clusterService.getLocalNode().id();
171
172 componentConfigService.registerProperties(getClass());
173 leadershipService.runForLeadership(appId.name());
174 groupService.addListener(groupListener);
175 deviceService.addListener(ovsdbListener);
176 deviceService.addListener(bridgeListener);
177 osNodeService.addListener(osNodeListener);
178
179 log.info("Started");
180 }
181
182 @Deactivate
183 protected void deactivate() {
184 osNodeService.removeListener(osNodeListener);
185 deviceService.removeListener(bridgeListener);
186 deviceService.removeListener(ovsdbListener);
187 groupService.removeListener(groupListener);
188 componentConfigService.unregisterProperties(getClass(), false);
189 leadershipService.withdraw(appId.name());
190 eventExecutor.shutdown();
191
192 log.info("Stopped");
193 }
194
195 @Modified
196 protected void modified(ComponentContext context) {
197 Dictionary<?, ?> properties = context.getProperties();
198 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
199 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
200 ovsdbPort = updatedOvsdbPort;
201 }
202
203 log.info("Modified");
204 }
205
206 @Override
207 public void processInitState(OpenstackNode osNode) {
208 if (!isOvsdbConnected(osNode)) {
209 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
210 return;
211 }
212 if (!deviceService.isAvailable(osNode.intgBridge())) {
213 createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
214 }
215 if (osNode.type() == GATEWAY &&
216 !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
217 createBridge(osNode, ROUTER_BRIDGE, osNode.routerBridge());
218 }
219 }
220
221 @Override
222 public void processDeviceCreatedState(OpenstackNode osNode) {
223 if (!isOvsdbConnected(osNode)) {
224 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
225 return;
226 }
227 if (osNode.type() == GATEWAY && (
228 !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
229 !isIntfCreated(osNode, PATCH_ROUT_BRIDGE)
230 )) {
231 createPatchInterface(osNode);
232 }
233 if (osNode.dataIp() != null &&
234 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
235 createTunnelInterface(osNode);
236 }
237 if (osNode.vlanIntf() != null &&
238 !isIntfEnabled(osNode, osNode.vlanIntf())) {
239 addSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf());
240 }
241 }
242
243 @Override
244 public void processPortCreatedState(OpenstackNode osNode) {
245 switch (osNode.type()) {
246 case COMPUTE:
247 if (osNode.dataIp() != null) {
248 addOrUpdateGatewayGroup(osNode,
249 osNodeService.completeNodes(GATEWAY),
250 VXLAN);
251 }
252 if (osNode.vlanIntf() != null) {
253 addOrUpdateGatewayGroup(osNode,
254 osNodeService.completeNodes(GATEWAY),
255 VLAN);
256 }
257 break;
258 case GATEWAY:
259 Set<OpenstackNode> gateways =
260 Sets.newHashSet(osNodeService.completeNodes(GATEWAY));
261 gateways.add(osNode);
262 osNodeService.completeNodes(COMPUTE).forEach(n -> {
263 if (n.dataIp() != null) {
264 addOrUpdateGatewayGroup(n, gateways, VXLAN);
265 }
266 if (n.vlanIntf() != null) {
267 addOrUpdateGatewayGroup(n, gateways, VLAN);
268 }
269 });
270 break;
271 default:
272 break;
273 }
274 }
275
276 @Override
277 public void processCompleteState(OpenstackNode osNode) {
278 OvsdbClientService ovsdbClient = ovsdbController.getOvsdbClient(
279 new OvsdbNodeId(osNode.managementIp(), DEFAULT_OVSDB_PORT));
280 if (ovsdbClient != null && ovsdbClient.isConnected()) {
281 ovsdbClient.disconnect();
282 }
283 }
284
285 @Override
286 public void processIncompleteState(OpenstackNode osNode) {
287 if (osNode.type() == COMPUTE) {
288 if (osNode.dataIp() != null) {
289 groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VXLAN), appId);
290 }
291 if (osNode.vlanIntf() != null) {
292 groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VLAN), appId);
293 }
294 }
295 if (osNode.type() == GATEWAY) {
296 osNodeService.completeNodes(COMPUTE).forEach(n -> {
297 if (n.dataIp() != null) {
298 addOrUpdateGatewayGroup(n,
299 osNodeService.completeNodes(GATEWAY),
300 VXLAN);
301 }
302 if (n.vlanIntf() != null) {
303 addOrUpdateGatewayGroup(n,
304 osNodeService.completeNodes(GATEWAY),
305 VLAN);
306 }
307 });
308 }
309 }
310
311 private boolean isOvsdbConnected(OpenstackNode osNode) {
312 OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
313 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
314 return deviceService.isAvailable(osNode.ovsdb()) &&
315 client != null &&
316 client.isConnected();
317 }
318
319 private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
320 Device device = deviceService.getDevice(osNode.ovsdb());
321 if (device == null || !device.is(BridgeConfig.class)) {
322 log.error("Failed to create integration bridge on {}", osNode.ovsdb());
323 return;
324 }
325
326 // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
327 Set<IpAddress> controllerIps;
328 if (bridgeName.equals(ROUTER_BRIDGE)) {
329 // TODO checks if empty controller does not break anything
330 controllerIps = ImmutableSet.of();
331 } else {
332 controllerIps = clusterService.getNodes().stream()
333 .map(ControllerNode::ip)
334 .collect(Collectors.toSet());
335 }
336
337 List<ControllerInfo> controllers = controllerIps.stream()
338 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
339 .collect(Collectors.toList());
340
341 String dpid = deviceId.toString().substring(DPID_BEGIN);
342 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
343 .name(bridgeName)
344 .failMode(BridgeDescription.FailMode.SECURE)
345 .datapathId(dpid)
346 .disableInBand()
347 .controllers(controllers)
348 .build();
349
350 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
351 bridgeConfig.addBridge(bridgeDesc);
352 }
353
354 private void addSystemInterface(OpenstackNode osNode, String bridgeName, String intfName) {
355 Device device = deviceService.getDevice(osNode.ovsdb());
356 if (device == null || !device.is(BridgeConfig.class)) {
357 return;
358 }
359 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
360 bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
361 }
362
363 private void createTunnelInterface(OpenstackNode osNode) {
364 if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
365 return;
366 }
367
368 Device device = deviceService.getDevice(osNode.ovsdb());
369 if (device == null || !device.is(InterfaceConfig.class)) {
370 log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
371 return;
372 }
373
374 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
375 .deviceId(INTEGRATION_BRIDGE)
376 .ifaceName(DEFAULT_TUNNEL)
377 .type(TunnelDescription.Type.VXLAN)
378 .remote(TunnelEndPoints.flowTunnelEndpoint())
379 .key(TunnelKeys.flowTunnelKey())
380 .build();
381
382 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
383 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
384 }
385
386 private void createPatchInterface(OpenstackNode osNode) {
387 checkArgument(osNode.type().equals(OpenstackNode.NodeType.GATEWAY));
388 if (isIntfEnabled(osNode, PATCH_INTG_BRIDGE) &&
389 isIntfCreated(osNode, PATCH_ROUT_BRIDGE)) {
390 return;
391 }
392
393 Device device = deviceService.getDevice(osNode.ovsdb());
394 if (device == null || !device.is(InterfaceConfig.class)) {
395 log.error("Failed to create patch interfaces on {}", osNode.hostname());
396 return;
397 }
398
399 PatchDescription patchIntg = DefaultPatchDescription.builder()
400 .deviceId(INTEGRATION_BRIDGE)
401 .ifaceName(PATCH_INTG_BRIDGE)
402 .peer(PATCH_ROUT_BRIDGE)
403 .build();
404
405 PatchDescription patchRout = DefaultPatchDescription.builder()
406 .deviceId(ROUTER_BRIDGE)
407 .ifaceName(PATCH_ROUT_BRIDGE)
408 .peer(PATCH_INTG_BRIDGE)
409 .build();
410
411 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
412 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
413 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
414 }
415
416 private void addOrUpdateGatewayGroup(OpenstackNode osNode,
417 Set<OpenstackNode> gatewayNodes,
418 NetworkMode mode) {
419 GroupBuckets buckets = gatewayGroupBuckets(osNode, gatewayNodes, mode);
420 if (groupService.getGroup(osNode.intgBridge(), osNode.gatewayGroupKey(mode)) == null) {
421 GroupDescription groupDescription = new DefaultGroupDescription(
422 osNode.intgBridge(),
423 GroupDescription.Type.SELECT,
424 buckets,
425 osNode.gatewayGroupKey(mode),
426 osNode.gatewayGroupId(mode).id(),
427 appId);
428 groupService.addGroup(groupDescription);
429 log.debug("Created gateway group for {}", osNode.hostname());
430 } else {
431 groupService.setBucketsForGroup(
432 osNode.intgBridge(),
433 osNode.gatewayGroupKey(mode),
434 buckets,
435 osNode.gatewayGroupKey(mode),
436 appId);
437 log.debug("Updated gateway group for {}", osNode.hostname());
438 }
439 }
440
441 private GroupBuckets gatewayGroupBuckets(OpenstackNode osNode,
442 Set<OpenstackNode> gatewayNodes,
443 NetworkMode mode) {
444 List<GroupBucket> bucketList = Lists.newArrayList();
445 switch (mode) {
446 case VXLAN:
447 gatewayNodes.stream().filter(n -> n.dataIp() != null).forEach(n -> {
448 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
449 .extension(tunnelDstTreatment(osNode.intgBridge(),
450 n.dataIp()),
451 osNode.intgBridge())
452 .setOutput(osNode.tunnelPortNum())
453 .build();
454 bucketList.add(createSelectGroupBucket(treatment));
455 });
456 return new GroupBuckets(bucketList);
457 case VLAN:
458 gatewayNodes.stream().filter(n -> n.vlanIntf() != null).forEach(n -> {
459 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
460 .setEthDst(n.vlanPortMac())
461 .setOutput(osNode.vlanPortNum())
462 .build();
463 bucketList.add(createSelectGroupBucket(treatment));
464 });
465 return new GroupBuckets(bucketList);
466 default:
467 return null;
468 }
469 }
470
471 private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
472 Device device = deviceService.getDevice(deviceId);
473 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
474 log.error("The extension treatment is not supported");
475 return null;
476 }
477
478 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
479 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
480 try {
481 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
482 return treatment;
483 } catch (ExtensionPropertyException e) {
484 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
485 return null;
486 }
487 }
488
489 private boolean isBridgeCreated(DeviceId ovsdbId, String bridgeName) {
490 Device device = deviceService.getDevice(ovsdbId);
491 if (device == null || !deviceService.isAvailable(device.id()) ||
492 !device.is(BridgeConfig.class)) {
493 return false;
494 }
495 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
496 return bridgeConfig.getBridges().stream()
497 .anyMatch(bridge -> bridge.name().equals(bridgeName));
498 }
499
500 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
501 if (!deviceService.isAvailable(osNode.intgBridge())) {
502 return false;
503 }
504 return deviceService.getPorts(osNode.intgBridge()).stream()
505 .anyMatch(port -> Objects.equals(
506 port.annotations().value(PORT_NAME), intf) &&
507 port.isEnabled());
508 }
509
510 private boolean isIntfCreated(OpenstackNode osNode, String intf) {
511 Device device = deviceService.getDevice(osNode.ovsdb());
512 if (device == null || !deviceService.isAvailable(osNode.ovsdb()) ||
513 !device.is(BridgeConfig.class)) {
514 return false;
515 }
516
517 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
518 return bridgeConfig.getPorts().stream()
519 .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
520 }
521
522 private boolean isGroupCreated(OpenstackNode osNode) {
523 for (OpenstackNode gNode : osNodeService.completeNodes(GATEWAY)) {
524 if (!isGatewayBucketAdded(osNode, gNode)) {
525 return false;
526 }
527 }
528 return true;
529 }
530
531 private boolean isGatewayBucketAdded(OpenstackNode cNode, OpenstackNode gNode) {
532 if (cNode.dataIp() != null) {
533 Group osGroup = groupService.getGroup(cNode.intgBridge(),
534 cNode.gatewayGroupKey(VXLAN));
535 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
536 .extension(tunnelDstTreatment(gNode.intgBridge(),
537 gNode.dataIp()),
538 cNode.intgBridge())
539 .setOutput(cNode.tunnelPortNum())
540 .build();
541 GroupBucket bucket = createSelectGroupBucket(treatment);
542 if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
543 !osGroup.buckets().buckets().contains(bucket)) {
544 return false;
545 }
546 }
547 if (cNode.vlanIntf() != null) {
548 Group osGroup = groupService.getGroup(cNode.intgBridge(),
549 cNode.gatewayGroupKey(VLAN));
550 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
551 .setEthDst(gNode.vlanPortMac())
552 .setOutput(cNode.vlanPortNum())
553 .build();
554 GroupBucket bucket = createSelectGroupBucket(treatment);
555 if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
556 !osGroup.buckets().buckets().contains(bucket)) {
557 return false;
558 }
559 }
560 return true;
561 }
562
563 private boolean isCurrentStateDone(OpenstackNode osNode) {
564 switch (osNode.state()) {
565 case INIT:
566 if (!deviceService.isAvailable(osNode.intgBridge())) {
567 return false;
568 }
569 if (osNode.type() == GATEWAY &&
570 !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
571 return false;
572 }
573 return true;
574 case DEVICE_CREATED:
575 if (osNode.dataIp() != null &&
576 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
577 return false;
578 }
579 if (osNode.vlanIntf() != null &&
580 !isIntfEnabled(osNode, osNode.vlanIntf())) {
581 return false;
582 }
583 if (osNode.type() == GATEWAY && (
584 !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
585 !isIntfCreated(osNode, PATCH_ROUT_BRIDGE))) {
586 return false;
587 }
588 return true;
589 case PORT_CREATED:
590 if (osNode.type() == COMPUTE) {
591 return isGroupCreated(osNode);
592 } else {
593 for (OpenstackNode cNode : osNodeService.completeNodes(COMPUTE)) {
594 if (!isGatewayBucketAdded(cNode, osNode)) {
595 return false;
596 }
597 }
598 return true;
599 }
600 case COMPLETE:
601 return false;
602 case INCOMPLETE:
603 // always return false
604 // run init CLI to re-trigger node bootstrap
605 return false;
606 default:
607 return true;
608 }
609 }
610
611 private void setState(OpenstackNode osNode, NodeState newState) {
612 if (osNode.state() == newState) {
613 return;
614 }
615 OpenstackNode updated = osNode.updateState(newState);
616 osNodeAdminService.updateNode(updated);
617 log.info("Changed {} state: {}", osNode.hostname(), newState);
618 }
619
620 private void bootstrapNode(OpenstackNode osNode) {
621 if (isCurrentStateDone(osNode)) {
622 setState(osNode, osNode.state().nextState());
623 } else {
624 log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
625 osNode.state().process(this, osNode);
626 }
627 }
628
629 private class InternalOvsdbListener implements DeviceListener {
630
631 @Override
632 public boolean isRelevant(DeviceEvent event) {
633 NodeId leader = leadershipService.getLeader(appId.name());
634 return Objects.equals(localNode, leader) &&
635 event.subject().type() == Device.Type.CONTROLLER &&
636 osNodeService.node(event.subject().id()) != null;
637 }
638
639 @Override
640 public void event(DeviceEvent event) {
641 Device device = event.subject();
642 OpenstackNode osNode = osNodeService.node(device.id());
643
644 switch (event.type()) {
645 case DEVICE_AVAILABILITY_CHANGED:
646 case DEVICE_ADDED:
647 eventExecutor.execute(() -> {
648 if (deviceService.isAvailable(device.id())) {
649 log.debug("OVSDB {} detected", device.id());
650 bootstrapNode(osNode);
651 } else if (osNode.state() == COMPLETE) {
652 log.debug("Removing OVSDB {}", device.id());
653 deviceAdminService.removeDevice(device.id());
654 }
655 });
656 break;
657 case PORT_ADDED:
658 case PORT_REMOVED:
659 case DEVICE_REMOVED:
660 default:
661 // do nothing
662 break;
663 }
664 }
665 }
666
667 private class InternalBridgeListener implements DeviceListener {
668
669 @Override
670 public boolean isRelevant(DeviceEvent event) {
671 NodeId leader = leadershipService.getLeader(appId.name());
672 return Objects.equals(localNode, leader) &&
673 event.subject().type() == Device.Type.SWITCH &&
674 osNodeService.node(event.subject().id()) != null;
675 }
676
677 @Override
678 public void event(DeviceEvent event) {
679 Device device = event.subject();
680 OpenstackNode osNode = osNodeService.node(device.id());
681
682 switch (event.type()) {
683 case DEVICE_AVAILABILITY_CHANGED:
684 case DEVICE_ADDED:
685 eventExecutor.execute(() -> {
686 if (deviceService.isAvailable(device.id())) {
687 log.debug("Integration bridge created on {}", osNode.hostname());
688 bootstrapNode(osNode);
689 } else if (osNode.state() == COMPLETE) {
690 log.warn("Device {} disconnected", device.id());
691 setState(osNode, INCOMPLETE);
692 }
693 });
694 break;
695 case PORT_ADDED:
696 eventExecutor.execute(() -> {
697 Port port = event.port();
698 String portName = port.annotations().value(PORT_NAME);
699 if (osNode.state() == DEVICE_CREATED && (
700 Objects.equals(portName, DEFAULT_TUNNEL) ||
701 Objects.equals(portName, osNode.vlanIntf()) ||
702 Objects.equals(portName, PATCH_INTG_BRIDGE) ||
703 Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
704 // FIXME we never gets PATCH_ROUTE_BRIDGE port added events as of now
705 log.debug("Interface {} added to {}", portName, event.subject().id());
706 bootstrapNode(osNode);
707 }
708 });
709 break;
710 case PORT_REMOVED:
711 eventExecutor.execute(() -> {
712 Port port = event.port();
713 String portName = port.annotations().value(PORT_NAME);
714 if (osNode.state() == COMPLETE && (
715 Objects.equals(portName, DEFAULT_TUNNEL) ||
716 Objects.equals(portName, osNode.vlanIntf()) ||
717 Objects.equals(portName, PATCH_INTG_BRIDGE) ||
718 Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
719 log.warn("Interface {} removed from {}", portName, event.subject().id());
720 setState(osNode, INCOMPLETE);
721 }
722 });
723 break;
724 case PORT_UPDATED:
725 case DEVICE_REMOVED:
726 default:
727 // do nothing
728 break;
729 }
730 }
731 }
732
733 private class InternalGroupListener implements GroupListener {
734
735 @Override
736 public boolean isRelevant(GroupEvent event) {
737 NodeId leader = leadershipService.getLeader(appId.name());
738 return Objects.equals(localNode, leader);
739 }
740
741 @Override
742 public void event(GroupEvent event) {
743 switch (event.type()) {
744 case GROUP_ADDED:
745 log.trace("Group added, ID:{} state:{}", event.subject().id(),
746 event.subject().state());
747 eventExecutor.execute(() -> {
748 OpenstackNode osNode = osNodeByGroupId(event.subject().id());
749 if (osNode != null && osNode.state() == PORT_CREATED) {
750 setState(osNode, COMPLETE);
751 }
752 });
753 break;
754 case GROUP_UPDATED:
755 log.trace("Group updated, ID:{} state:{}", event.subject().id(),
756 event.subject().state());
757 eventExecutor.execute(() -> {
758 osNodeService.nodes(GATEWAY).stream()
759 .filter(osNode -> osNode.state() == PORT_CREATED)
760 .forEach(osNode -> bootstrapNode(osNode));
761 });
762 break;
763 case GROUP_REMOVED:
764 // TODO handle group removed
765 break;
766 default:
767 break;
768 }
769 }
770
771 private OpenstackNode osNodeByGroupId(GroupId groupId) {
772 return osNodeService.nodes().stream()
773 .filter(n -> n.gatewayGroupId(VXLAN).equals(groupId) ||
774 n.gatewayGroupId(VLAN).equals(groupId))
775 .findAny().orElse(null);
776 }
777 }
778
779 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
780
781 @Override
782 public boolean isRelevant(OpenstackNodeEvent event) {
783 NodeId leader = leadershipService.getLeader(appId.name());
784 return Objects.equals(localNode, leader);
785 }
786
787 @Override
788 public void event(OpenstackNodeEvent event) {
789 switch (event.type()) {
790 case OPENSTACK_NODE_CREATED:
791 case OPENSTACK_NODE_UPDATED:
792 eventExecutor.execute(() -> {
793 bootstrapNode(event.subject());
794 });
795 break;
796 case OPENSTACK_NODE_COMPLETE:
797 break;
798 case OPENSTACK_NODE_REMOVED:
799 break;
800 default:
801 break;
802 }
803 }
804 }
805}