blob: 625d41d6bd7d13d52fbfbf0cbe2368b36e9209d3 [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
Ray Milkey74e59132018-01-17 15:24:52 -0800477 if (device == null) {
478 return null;
479 }
480
Hyunsun Moon0d457362017-06-27 17:19:41 +0900481 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
482 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
483 try {
484 treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
485 return treatment;
486 } catch (ExtensionPropertyException e) {
487 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
488 return null;
489 }
490 }
491
492 private boolean isBridgeCreated(DeviceId ovsdbId, String bridgeName) {
493 Device device = deviceService.getDevice(ovsdbId);
494 if (device == null || !deviceService.isAvailable(device.id()) ||
495 !device.is(BridgeConfig.class)) {
496 return false;
497 }
Hyunsun Moon090d77d2017-07-05 17:48:37 +0900498 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900499 return bridgeConfig.getBridges().stream()
500 .anyMatch(bridge -> bridge.name().equals(bridgeName));
501 }
502
503 private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
504 if (!deviceService.isAvailable(osNode.intgBridge())) {
505 return false;
506 }
507 return deviceService.getPorts(osNode.intgBridge()).stream()
508 .anyMatch(port -> Objects.equals(
509 port.annotations().value(PORT_NAME), intf) &&
510 port.isEnabled());
511 }
512
513 private boolean isIntfCreated(OpenstackNode osNode, String intf) {
514 Device device = deviceService.getDevice(osNode.ovsdb());
515 if (device == null || !deviceService.isAvailable(osNode.ovsdb()) ||
516 !device.is(BridgeConfig.class)) {
517 return false;
518 }
519
520 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
521 return bridgeConfig.getPorts().stream()
522 .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
523 }
524
525 private boolean isGroupCreated(OpenstackNode osNode) {
526 for (OpenstackNode gNode : osNodeService.completeNodes(GATEWAY)) {
527 if (!isGatewayBucketAdded(osNode, gNode)) {
528 return false;
529 }
530 }
531 return true;
532 }
533
534 private boolean isGatewayBucketAdded(OpenstackNode cNode, OpenstackNode gNode) {
535 if (cNode.dataIp() != null) {
536 Group osGroup = groupService.getGroup(cNode.intgBridge(),
537 cNode.gatewayGroupKey(VXLAN));
538 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
539 .extension(tunnelDstTreatment(gNode.intgBridge(),
540 gNode.dataIp()),
541 cNode.intgBridge())
542 .setOutput(cNode.tunnelPortNum())
543 .build();
544 GroupBucket bucket = createSelectGroupBucket(treatment);
Hyunsun Moonf3d1fcc2017-07-11 14:44:13 +0900545 if (osGroup == null || !osGroup.buckets().buckets().contains(bucket)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900546 return false;
547 }
548 }
549 if (cNode.vlanIntf() != null) {
550 Group osGroup = groupService.getGroup(cNode.intgBridge(),
551 cNode.gatewayGroupKey(VLAN));
552 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
553 .setEthDst(gNode.vlanPortMac())
554 .setOutput(cNode.vlanPortNum())
555 .build();
556 GroupBucket bucket = createSelectGroupBucket(treatment);
Hyunsun Moonf3d1fcc2017-07-11 14:44:13 +0900557 if (osGroup == null || !osGroup.buckets().buckets().contains(bucket)) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900558 return false;
559 }
560 }
561 return true;
562 }
563
564 private boolean isCurrentStateDone(OpenstackNode osNode) {
565 switch (osNode.state()) {
566 case INIT:
567 if (!deviceService.isAvailable(osNode.intgBridge())) {
568 return false;
569 }
570 if (osNode.type() == GATEWAY &&
571 !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
572 return false;
573 }
574 return true;
575 case DEVICE_CREATED:
576 if (osNode.dataIp() != null &&
577 !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
578 return false;
579 }
580 if (osNode.vlanIntf() != null &&
581 !isIntfEnabled(osNode, osNode.vlanIntf())) {
582 return false;
583 }
584 if (osNode.type() == GATEWAY && (
585 !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
586 !isIntfCreated(osNode, PATCH_ROUT_BRIDGE))) {
587 return false;
588 }
589 return true;
590 case PORT_CREATED:
591 if (osNode.type() == COMPUTE) {
592 return isGroupCreated(osNode);
593 } else {
594 for (OpenstackNode cNode : osNodeService.completeNodes(COMPUTE)) {
595 if (!isGatewayBucketAdded(cNode, osNode)) {
596 return false;
597 }
598 }
599 return true;
600 }
601 case COMPLETE:
602 return false;
603 case INCOMPLETE:
604 // always return false
605 // run init CLI to re-trigger node bootstrap
606 return false;
607 default:
608 return true;
609 }
610 }
611
612 private void setState(OpenstackNode osNode, NodeState newState) {
613 if (osNode.state() == newState) {
614 return;
615 }
616 OpenstackNode updated = osNode.updateState(newState);
617 osNodeAdminService.updateNode(updated);
618 log.info("Changed {} state: {}", osNode.hostname(), newState);
619 }
620
621 private void bootstrapNode(OpenstackNode osNode) {
622 if (isCurrentStateDone(osNode)) {
623 setState(osNode, osNode.state().nextState());
624 } else {
625 log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
626 osNode.state().process(this, osNode);
627 }
628 }
629
630 private class InternalOvsdbListener implements DeviceListener {
631
632 @Override
633 public boolean isRelevant(DeviceEvent event) {
634 NodeId leader = leadershipService.getLeader(appId.name());
635 return Objects.equals(localNode, leader) &&
636 event.subject().type() == Device.Type.CONTROLLER &&
637 osNodeService.node(event.subject().id()) != null;
638 }
639
640 @Override
641 public void event(DeviceEvent event) {
642 Device device = event.subject();
643 OpenstackNode osNode = osNodeService.node(device.id());
644
645 switch (event.type()) {
646 case DEVICE_AVAILABILITY_CHANGED:
647 case DEVICE_ADDED:
648 eventExecutor.execute(() -> {
649 if (deviceService.isAvailable(device.id())) {
650 log.debug("OVSDB {} detected", device.id());
651 bootstrapNode(osNode);
652 } else if (osNode.state() == COMPLETE) {
653 log.debug("Removing OVSDB {}", device.id());
654 deviceAdminService.removeDevice(device.id());
655 }
656 });
657 break;
658 case PORT_ADDED:
659 case PORT_REMOVED:
660 case DEVICE_REMOVED:
661 default:
662 // do nothing
663 break;
664 }
665 }
666 }
667
668 private class InternalBridgeListener implements DeviceListener {
669
670 @Override
671 public boolean isRelevant(DeviceEvent event) {
672 NodeId leader = leadershipService.getLeader(appId.name());
673 return Objects.equals(localNode, leader) &&
674 event.subject().type() == Device.Type.SWITCH &&
675 osNodeService.node(event.subject().id()) != null;
676 }
677
678 @Override
679 public void event(DeviceEvent event) {
680 Device device = event.subject();
681 OpenstackNode osNode = osNodeService.node(device.id());
682
683 switch (event.type()) {
684 case DEVICE_AVAILABILITY_CHANGED:
685 case DEVICE_ADDED:
686 eventExecutor.execute(() -> {
687 if (deviceService.isAvailable(device.id())) {
688 log.debug("Integration bridge created on {}", osNode.hostname());
689 bootstrapNode(osNode);
690 } else if (osNode.state() == COMPLETE) {
691 log.warn("Device {} disconnected", device.id());
692 setState(osNode, INCOMPLETE);
693 }
694 });
695 break;
696 case PORT_ADDED:
697 eventExecutor.execute(() -> {
698 Port port = event.port();
699 String portName = port.annotations().value(PORT_NAME);
700 if (osNode.state() == DEVICE_CREATED && (
701 Objects.equals(portName, DEFAULT_TUNNEL) ||
702 Objects.equals(portName, osNode.vlanIntf()) ||
703 Objects.equals(portName, PATCH_INTG_BRIDGE) ||
704 Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
705 // FIXME we never gets PATCH_ROUTE_BRIDGE port added events as of now
706 log.debug("Interface {} added to {}", portName, event.subject().id());
707 bootstrapNode(osNode);
708 }
709 });
710 break;
711 case PORT_REMOVED:
712 eventExecutor.execute(() -> {
713 Port port = event.port();
714 String portName = port.annotations().value(PORT_NAME);
715 if (osNode.state() == COMPLETE && (
716 Objects.equals(portName, DEFAULT_TUNNEL) ||
717 Objects.equals(portName, osNode.vlanIntf()) ||
718 Objects.equals(portName, PATCH_INTG_BRIDGE) ||
719 Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
720 log.warn("Interface {} removed from {}", portName, event.subject().id());
721 setState(osNode, INCOMPLETE);
722 }
723 });
724 break;
725 case PORT_UPDATED:
726 case DEVICE_REMOVED:
727 default:
728 // do nothing
729 break;
730 }
731 }
732 }
733
734 private class InternalGroupListener implements GroupListener {
735
736 @Override
737 public boolean isRelevant(GroupEvent event) {
738 NodeId leader = leadershipService.getLeader(appId.name());
739 return Objects.equals(localNode, leader);
740 }
741
742 @Override
743 public void event(GroupEvent event) {
744 switch (event.type()) {
745 case GROUP_ADDED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900746 eventExecutor.execute(() -> {
Hyunsun Moon090d77d2017-07-05 17:48:37 +0900747 log.trace("Group added, ID:{} state:{}", event.subject().id(),
748 event.subject().state());
749 processGroup(event.subject());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900750 });
751 break;
752 case GROUP_UPDATED:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900753 eventExecutor.execute(() -> {
Hyunsun Moon090d77d2017-07-05 17:48:37 +0900754 log.trace("Group updated, ID:{} state:{}", event.subject().id(),
755 event.subject().state());
756 processGroup(event.subject());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900757 });
758 break;
759 case GROUP_REMOVED:
760 // TODO handle group removed
761 break;
762 default:
763 break;
764 }
765 }
766
Hyunsun Moon090d77d2017-07-05 17:48:37 +0900767 private void processGroup(Group group) {
Hyunsun Moonf3d1fcc2017-07-11 14:44:13 +0900768 OpenstackNode osNode = osNodeService.nodes(COMPUTE).stream()
769 .filter(n -> n.state() == PORT_CREATED &&
770 (n.gatewayGroupId(VXLAN).equals(group.id()) ||
771 n.gatewayGroupId(VLAN).equals(group.id())))
Hyunsun Moon0d457362017-06-27 17:19:41 +0900772 .findAny().orElse(null);
Hyunsun Moonf3d1fcc2017-07-11 14:44:13 +0900773 if (osNode != null) {
Hyunsun Moon090d77d2017-07-05 17:48:37 +0900774 bootstrapNode(osNode);
775 }
776 osNodeService.nodes(GATEWAY).stream()
777 .filter(gNode -> gNode.state() == PORT_CREATED)
778 .forEach(DefaultOpenstackNodeHandler.this::bootstrapNode);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900779 }
780 }
781
782 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
783
784 @Override
785 public boolean isRelevant(OpenstackNodeEvent event) {
786 NodeId leader = leadershipService.getLeader(appId.name());
787 return Objects.equals(localNode, leader);
788 }
789
790 @Override
791 public void event(OpenstackNodeEvent event) {
792 switch (event.type()) {
793 case OPENSTACK_NODE_CREATED:
794 case OPENSTACK_NODE_UPDATED:
795 eventExecutor.execute(() -> {
796 bootstrapNode(event.subject());
797 });
798 break;
799 case OPENSTACK_NODE_COMPLETE:
800 break;
801 case OPENSTACK_NODE_REMOVED:
802 break;
803 default:
804 break;
805 }
806 }
807 }
808}