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