Hyunsun Moon
Committed by Gerrit Code Review

CORD-305 Added basic VTN rules for VMs with openstackswitching

Change-Id: I3eebc3c396b6657457363c183ca8c260b6bb8db4
...@@ -33,6 +33,10 @@ ...@@ -33,6 +33,10 @@
33 33
34 <properties> 34 <properties>
35 <onos.app.name>org.onosproject.cordvtn</onos.app.name> 35 <onos.app.name>org.onosproject.cordvtn</onos.app.name>
36 + <onos.app.requires>
37 + org.onosproject.ovsdb,
38 + org.onosproject.openstackswitching
39 + </onos.app.requires>
36 </properties> 40 </properties>
37 41
38 <dependencies> 42 <dependencies>
...@@ -64,6 +68,11 @@ ...@@ -64,6 +68,11 @@
64 <artifactId>org.apache.karaf.shell.console</artifactId> 68 <artifactId>org.apache.karaf.shell.console</artifactId>
65 <version>3.0.3</version> 69 <version>3.0.3</version>
66 </dependency> 70 </dependency>
71 + <dependency>
72 + <groupId>org.onosproject</groupId>
73 + <artifactId>onos-app-openstackswitching-api</artifactId>
74 + <version>${project.version}</version>
75 + </dependency>
67 </dependencies> 76 </dependencies>
68 77
69 </project> 78 </project>
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
15 */ 15 */
16 package org.onosproject.cordvtn; 16 package org.onosproject.cordvtn;
17 17
18 +import com.google.common.collect.Lists;
19 +import com.google.common.collect.Maps;
18 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
19 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
20 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
...@@ -23,6 +25,7 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -23,6 +25,7 @@ import org.apache.felix.scr.annotations.Reference;
23 import org.apache.felix.scr.annotations.ReferenceCardinality; 25 import org.apache.felix.scr.annotations.ReferenceCardinality;
24 import org.apache.felix.scr.annotations.Service; 26 import org.apache.felix.scr.annotations.Service;
25 import org.onlab.util.ItemNotFoundException; 27 import org.onlab.util.ItemNotFoundException;
28 +import org.onlab.packet.IpAddress;
26 import org.onlab.util.KryoNamespace; 29 import org.onlab.util.KryoNamespace;
27 import org.onosproject.cluster.ClusterService; 30 import org.onosproject.cluster.ClusterService;
28 import org.onosproject.core.ApplicationId; 31 import org.onosproject.core.ApplicationId;
...@@ -31,9 +34,11 @@ import org.onosproject.net.DefaultAnnotations; ...@@ -31,9 +34,11 @@ import org.onosproject.net.DefaultAnnotations;
31 import org.onosproject.net.Device; 34 import org.onosproject.net.Device;
32 import org.onosproject.net.DeviceId; 35 import org.onosproject.net.DeviceId;
33 import org.onosproject.net.Host; 36 import org.onosproject.net.Host;
37 +import org.onosproject.net.HostId;
34 import org.onosproject.net.Port; 38 import org.onosproject.net.Port;
35 import org.onosproject.net.behaviour.BridgeConfig; 39 import org.onosproject.net.behaviour.BridgeConfig;
36 import org.onosproject.net.behaviour.BridgeName; 40 import org.onosproject.net.behaviour.BridgeName;
41 +import org.onosproject.net.ConnectPoint;
37 import org.onosproject.net.behaviour.ControllerInfo; 42 import org.onosproject.net.behaviour.ControllerInfo;
38 import org.onosproject.net.behaviour.DefaultTunnelDescription; 43 import org.onosproject.net.behaviour.DefaultTunnelDescription;
39 import org.onosproject.net.behaviour.TunnelConfig; 44 import org.onosproject.net.behaviour.TunnelConfig;
...@@ -45,9 +50,13 @@ import org.onosproject.net.device.DeviceListener; ...@@ -45,9 +50,13 @@ import org.onosproject.net.device.DeviceListener;
45 import org.onosproject.net.device.DeviceService; 50 import org.onosproject.net.device.DeviceService;
46 import org.onosproject.net.driver.DriverHandler; 51 import org.onosproject.net.driver.DriverHandler;
47 import org.onosproject.net.driver.DriverService; 52 import org.onosproject.net.driver.DriverService;
53 +import org.onosproject.net.flowobjective.FlowObjectiveService;
48 import org.onosproject.net.host.HostEvent; 54 import org.onosproject.net.host.HostEvent;
49 import org.onosproject.net.host.HostListener; 55 import org.onosproject.net.host.HostListener;
50 import org.onosproject.net.host.HostService; 56 import org.onosproject.net.host.HostService;
57 +import org.onosproject.openstackswitching.OpenstackNetwork;
58 +import org.onosproject.openstackswitching.OpenstackPort;
59 +import org.onosproject.openstackswitching.OpenstackSwitchingService;
51 import org.onosproject.ovsdb.controller.OvsdbClientService; 60 import org.onosproject.ovsdb.controller.OvsdbClientService;
52 import org.onosproject.ovsdb.controller.OvsdbController; 61 import org.onosproject.ovsdb.controller.OvsdbController;
53 import org.onosproject.ovsdb.controller.OvsdbNodeId; 62 import org.onosproject.ovsdb.controller.OvsdbNodeId;
...@@ -62,8 +71,10 @@ import java.util.HashMap; ...@@ -62,8 +71,10 @@ import java.util.HashMap;
62 import java.util.List; 71 import java.util.List;
63 import java.util.Map; 72 import java.util.Map;
64 import java.util.NoSuchElementException; 73 import java.util.NoSuchElementException;
74 +import java.util.Set;
65 import java.util.concurrent.ExecutorService; 75 import java.util.concurrent.ExecutorService;
66 import java.util.concurrent.Executors; 76 import java.util.concurrent.Executors;
77 +import java.util.stream.Collectors;
67 78
68 import static com.google.common.base.Preconditions.checkNotNull; 79 import static com.google.common.base.Preconditions.checkNotNull;
69 import static org.onlab.util.Tools.groupedThreads; 80 import static org.onlab.util.Tools.groupedThreads;
...@@ -72,8 +83,8 @@ import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN; ...@@ -72,8 +83,8 @@ import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
72 import static org.slf4j.LoggerFactory.getLogger; 83 import static org.slf4j.LoggerFactory.getLogger;
73 84
74 /** 85 /**
75 - * Provides initial setup or cleanup for provisioning virtual tenant networks 86 + * Provisions virtual tenant networks with service chaining capability
76 - * on ovsdb, integration bridge and vm when they are added or deleted. 87 + * in OpenStack environment.
77 */ 88 */
78 @Component(immediate = true) 89 @Component(immediate = true)
79 @Service 90 @Service
...@@ -86,7 +97,8 @@ public class CordVtn implements CordVtnService { ...@@ -86,7 +97,8 @@ public class CordVtn implements CordVtnService {
86 .register(KryoNamespaces.API) 97 .register(KryoNamespaces.API)
87 .register(CordVtnNode.class) 98 .register(CordVtnNode.class)
88 .register(NodeState.class); 99 .register(NodeState.class);
89 - private static final String DEFAULT_BRIDGE_NAME = "br-int"; 100 + private static final String DEFAULT_BRIDGE = "br-int";
101 + private static final String VPORT_PREFIX = "tap";
90 private static final String DEFAULT_TUNNEL = "vxlan"; 102 private static final String DEFAULT_TUNNEL = "vxlan";
91 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() { 103 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
92 { 104 {
...@@ -116,11 +128,17 @@ public class CordVtn implements CordVtnService { ...@@ -116,11 +128,17 @@ public class CordVtn implements CordVtnService {
116 protected DeviceAdminService adminService; 128 protected DeviceAdminService adminService;
117 129
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 + protected FlowObjectiveService flowObjectiveService;
132 +
133 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OvsdbController controller; 134 protected OvsdbController controller;
120 135
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected ClusterService clusterService; 137 protected ClusterService clusterService;
123 138
139 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 + protected OpenstackSwitchingService openstackService;
141 +
124 private final ExecutorService eventExecutor = Executors 142 private final ExecutorService eventExecutor = Executors
125 .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler")); 143 .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
126 144
...@@ -132,6 +150,8 @@ public class CordVtn implements CordVtnService { ...@@ -132,6 +150,8 @@ public class CordVtn implements CordVtnService {
132 private final VmHandler vmHandler = new VmHandler(); 150 private final VmHandler vmHandler = new VmHandler();
133 151
134 private ConsistentMap<CordVtnNode, NodeState> nodeStore; 152 private ConsistentMap<CordVtnNode, NodeState> nodeStore;
153 + private Map<HostId, String> hostNetworkMap = Maps.newHashMap();
154 + private CordVtnRuleInstaller ruleInstaller;
135 155
136 private enum NodeState { 156 private enum NodeState {
137 157
...@@ -185,6 +205,8 @@ public class CordVtn implements CordVtnService { ...@@ -185,6 +205,8 @@ public class CordVtn implements CordVtnService {
185 .withApplicationId(appId) 205 .withApplicationId(appId)
186 .build(); 206 .build();
187 207
208 + ruleInstaller = new CordVtnRuleInstaller(appId, flowObjectiveService,
209 + driverService, DEFAULT_TUNNEL);
188 deviceService.addListener(deviceListener); 210 deviceService.addListener(deviceListener);
189 hostService.addListener(hostListener); 211 hostService.addListener(hostListener);
190 212
...@@ -314,11 +336,27 @@ public class CordVtn implements CordVtnService { ...@@ -314,11 +336,27 @@ public class CordVtn implements CordVtnService {
314 336
315 /** 337 /**
316 * Performs tasks after node initialization. 338 * Performs tasks after node initialization.
339 + * First disconnect unnecessary OVSDB connection and then installs flow rules
340 + * for existing VMs if there are any.
317 * 341 *
318 * @param node cordvtn node 342 * @param node cordvtn node
319 */ 343 */
320 private void postInit(CordVtnNode node) { 344 private void postInit(CordVtnNode node) {
321 disconnect(node); 345 disconnect(node);
346 +
347 + Set<OpenstackNetwork> vNets = Sets.newHashSet();
348 + hostService.getConnectedHosts(node.intBrId())
349 + .stream()
350 + .forEach(host -> {
351 + OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
352 + if (vNet != null) {
353 + log.info("VM {} is detected", host.id());
354 +
355 + hostNetworkMap.put(host.id(), vNet.id());
356 + vNets.add(vNet);
357 + }
358 + });
359 + vNets.stream().forEach(this::installFlowRules);
322 } 360 }
323 361
324 /** 362 /**
...@@ -443,7 +481,7 @@ public class CordVtn implements CordVtnService { ...@@ -443,7 +481,7 @@ public class CordVtn implements CordVtnService {
443 } 481 }
444 482
445 List<ControllerInfo> controllers = new ArrayList<>(); 483 List<ControllerInfo> controllers = new ArrayList<>();
446 - Sets.newHashSet(clusterService.getNodes()) 484 + Sets.newHashSet(clusterService.getNodes()).stream()
447 .forEach(controller -> { 485 .forEach(controller -> {
448 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp"); 486 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
449 controllers.add(ctrlInfo); 487 controllers.add(ctrlInfo);
...@@ -453,7 +491,7 @@ public class CordVtn implements CordVtnService { ...@@ -453,7 +491,7 @@ public class CordVtn implements CordVtnService {
453 try { 491 try {
454 DriverHandler handler = driverService.createHandler(node.ovsdbId()); 492 DriverHandler handler = driverService.createHandler(node.ovsdbId());
455 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class); 493 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
456 - bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME), dpid, controllers); 494 + bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
457 } catch (ItemNotFoundException e) { 495 } catch (ItemNotFoundException e) {
458 log.warn("Failed to create integration bridge on {}", node.ovsdbId()); 496 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
459 } 497 }
...@@ -474,13 +512,12 @@ public class CordVtn implements CordVtnService { ...@@ -474,13 +512,12 @@ public class CordVtn implements CordVtnService {
474 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key)); 512 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
475 } 513 }
476 TunnelDescription description = 514 TunnelDescription description =
477 - new DefaultTunnelDescription(null, null, VXLAN, 515 + new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
478 - TunnelName.tunnelName(DEFAULT_TUNNEL),
479 optionBuilder.build()); 516 optionBuilder.build());
480 try { 517 try {
481 DriverHandler handler = driverService.createHandler(node.ovsdbId()); 518 DriverHandler handler = driverService.createHandler(node.ovsdbId());
482 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class); 519 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
483 - tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME), description); 520 + tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
484 } catch (ItemNotFoundException e) { 521 } catch (ItemNotFoundException e) {
485 log.warn("Failed to create tunnel interface on {}", node.ovsdbId()); 522 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
486 } 523 }
...@@ -516,6 +553,212 @@ public class CordVtn implements CordVtnService { ...@@ -516,6 +553,212 @@ public class CordVtn implements CordVtnService {
516 } 553 }
517 } 554 }
518 555
556 + /**
557 + * Returns tunnel port of the device.
558 + *
559 + * @param bridgeId device id
560 + * @return port, null if no tunnel port exists on a given device
561 + */
562 + private Port getTunnelPort(DeviceId bridgeId) {
563 + try {
564 + return deviceService.getPorts(bridgeId).stream()
565 + .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
566 + && p.isEnabled())
567 + .findFirst().get();
568 + } catch (NoSuchElementException e) {
569 + return null;
570 + }
571 + }
572 +
573 + /**
574 + * Returns remote ip address for tunneling.
575 + *
576 + * @param bridgeId device id
577 + * @return ip address, null if no such device exists
578 + */
579 + private IpAddress getRemoteIp(DeviceId bridgeId) {
580 + CordVtnNode node = getNodeByBridgeId(bridgeId);
581 + if (node != null) {
582 + // TODO get data plane IP for tunneling
583 + return node.ovsdbIp();
584 + } else {
585 + return null;
586 + }
587 + }
588 +
589 + /**
590 + * Returns destination information of all ports associated with a given
591 + * OpenStack network. Output of the destination information is set to local
592 + * port or tunnel port according to a given device id.
593 + *
594 + * @param deviceId device id to install flow rules
595 + * @param vNet OpenStack network
596 + * @return list of flow information, empty list if no flow information exists
597 + */
598 + private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
599 + List<DestinationInfo> dstInfos = Lists.newArrayList();
600 + long tunnelId = Long.valueOf(vNet.segmentId());
601 +
602 + for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
603 + ConnectPoint cp = getConnectPoint(vPort);
604 + if (cp == null) {
605 + log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
606 + continue;
607 + }
608 +
609 + DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
610 + DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
611 + DestinationInfo.builder(getTunnelPort(deviceId))
612 + .setRemoteIp(getRemoteIp(cp.deviceId()));
613 +
614 + dBuilder.setMac(vPort.macAddress())
615 + .setTunnelId(tunnelId);
616 + dstInfos.add(dBuilder.build());
617 + }
618 + return dstInfos;
619 + }
620 +
621 + /**
622 + * Returns local ports associated with a given OpenStack network.
623 + *
624 + * @param bridgeId device id
625 + * @param vNet OpenStack network
626 + * @return port list, empty list if no port exists
627 + */
628 + private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
629 + List<Port> ports = new ArrayList<>();
630 + openstackService.ports(vNet.id()).stream().forEach(port -> {
631 + ConnectPoint cp = getConnectPoint(port);
632 + if (cp != null && cp.deviceId().equals(bridgeId)) {
633 + ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
634 + }
635 + });
636 + return ports;
637 + }
638 +
639 + /**
640 + * Returns OpenStack port associated with a given host.
641 + *
642 + * @param host host
643 + * @return OpenStack port, or null if no port has been found
644 + */
645 + private OpenstackPort getOpenstackPortByHost(Host host) {
646 + Port port = deviceService.getPort(host.location().deviceId(),
647 + host.location().port());
648 + return openstackService.port(port);
649 + }
650 +
651 + /**
652 + * Returns OpenStack network associated with a given host.
653 + *
654 + * @param host host
655 + * @return OpenStack network, or null if no network has been found
656 + */
657 + private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
658 + OpenstackPort vPort = getOpenstackPortByHost(host);
659 + if (vPort != null) {
660 + return openstackService.network(vPort.networkId());
661 + } else {
662 + return null;
663 + }
664 + }
665 +
666 + /**
667 + * Returns port name with OpenStack port information.
668 + *
669 + * @param vPort OpenStack port
670 + * @return port name
671 + */
672 + private String getPortName(OpenstackPort vPort) {
673 + checkNotNull(vPort);
674 + return VPORT_PREFIX + vPort.id().substring(0, 10);
675 + }
676 +
677 + /**
678 + * Returns connect point of a given OpenStack port.
679 + * It assumes there's only one physical port associated with an OpenStack port.
680 + *
681 + * @param vPort openstack port
682 + * @return connect point, null if no such port exists
683 + */
684 + private ConnectPoint getConnectPoint(OpenstackPort vPort) {
685 + try {
686 + Host host = hostService.getHostsByMac(vPort.macAddress())
687 + .stream()
688 + .findFirst()
689 + .get();
690 + return new ConnectPoint(host.location().deviceId(), host.location().port());
691 + } catch (NoSuchElementException e) {
692 + log.debug("Not a valid host with {}", vPort.macAddress());
693 + return null;
694 + }
695 + }
696 +
697 + /**
698 + * Installs flow rules for a given OpenStack network.
699 + *
700 + * @param vNet OpenStack network
701 + */
702 + private void installFlowRules(OpenstackNetwork vNet) {
703 + checkNotNull(vNet, "Tenant network should not be null");
704 +
705 + for (Device device : deviceService.getAvailableDevices(SWITCH)) {
706 + List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
707 +
708 + for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
709 + List<DestinationInfo> localInInfos = dstInfos.stream()
710 + .filter(info -> !info.output().equals(inPort))
711 + .collect(Collectors.toList());
712 + ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
713 + }
714 +
715 + Port tunPort = getTunnelPort(device.id());
716 + List<DestinationInfo> tunnelInInfos = dstInfos.stream()
717 + .filter(info -> !info.output().equals(tunPort))
718 + .collect(Collectors.toList());
719 + ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
720 + }
721 + }
722 +
723 + /**
724 + * Uninstalls flow rules associated with a given host for a given OpenStack network.
725 + *
726 + * @param vNet OpenStack network
727 + * @param host removed host
728 + */
729 + private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
730 + checkNotNull(vNet, "Tenant network should not be null");
731 +
732 + Port removedPort = deviceService.getPort(host.location().deviceId(),
733 + host.location().port());
734 +
735 + for (Device device : deviceService.getAvailableDevices(SWITCH)) {
736 + List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
737 +
738 + for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
739 + List<DestinationInfo> localInInfos = Lists.newArrayList(
740 + DestinationInfo.builder(getTunnelPort(device.id()))
741 + .setTunnelId(Long.valueOf(vNet.segmentId()))
742 + .setMac(host.mac())
743 + .setRemoteIp(getRemoteIp(host.location().deviceId()))
744 + .build());
745 + ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
746 + }
747 +
748 + if (device.id().equals(host.location().deviceId())) {
749 + Port tunPort = getTunnelPort(device.id());
750 + List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
751 + DestinationInfo.builder(removedPort)
752 + .setTunnelId(Long.valueOf(vNet.segmentId()))
753 + .setMac(host.mac())
754 + .build());
755 +
756 + ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
757 + ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
758 + }
759 + }
760 + }
761 +
519 private class InternalDeviceListener implements DeviceListener { 762 private class InternalDeviceListener implements DeviceListener {
520 763
521 @Override 764 @Override
...@@ -644,12 +887,40 @@ public class CordVtn implements CordVtnService { ...@@ -644,12 +887,40 @@ public class CordVtn implements CordVtnService {
644 887
645 @Override 888 @Override
646 public void connected(Host host) { 889 public void connected(Host host) {
890 + CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
891 + if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
892 + // do nothing for the host on unregistered or unprepared device
893 + return;
894 + }
895 +
896 + OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
897 + if (vNet == null) {
898 + return;
899 + }
900 +
647 log.info("VM {} is detected", host.id()); 901 log.info("VM {} is detected", host.id());
902 +
903 + hostNetworkMap.put(host.id(), vNet.id());
904 + installFlowRules(vNet);
648 } 905 }
649 906
650 @Override 907 @Override
651 public void disconnected(Host host) { 908 public void disconnected(Host host) {
909 + CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
910 + if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
911 + // do nothing for the host on unregistered or unprepared device
912 + return;
913 + }
914 +
915 + OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
916 + if (vNet == null) {
917 + return;
918 + }
919 +
652 log.info("VM {} is vanished", host.id()); 920 log.info("VM {} is vanished", host.id());
921 +
922 + uninstallFlowRules(vNet, host);
923 + hostNetworkMap.remove(host.id());
653 } 924 }
654 } 925 }
655 } 926 }
......
1 +/*
2 + * Copyright 2015 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 + */
16 +package org.onosproject.cordvtn;
17 +
18 +import org.onlab.packet.Ip4Address;
19 +import org.onlab.util.ItemNotFoundException;
20 +import org.onosproject.core.ApplicationId;
21 +import org.onosproject.net.DeviceId;
22 +import org.onosproject.net.Port;
23 +import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
24 +import org.onosproject.net.driver.DefaultDriverData;
25 +import org.onosproject.net.driver.DefaultDriverHandler;
26 +import org.onosproject.net.driver.Driver;
27 +import org.onosproject.net.driver.DriverHandler;
28 +import org.onosproject.net.driver.DriverService;
29 +import org.onosproject.net.flow.DefaultTrafficSelector;
30 +import org.onosproject.net.flow.DefaultTrafficTreatment;
31 +import org.onosproject.net.flow.TrafficSelector;
32 +import org.onosproject.net.flow.TrafficTreatment;
33 +import org.onosproject.net.flow.instructions.ExtensionPropertyException;
34 +import org.onosproject.net.flow.instructions.ExtensionTreatment;
35 +import org.onosproject.net.flowobjective.DefaultForwardingObjective;
36 +import org.onosproject.net.flowobjective.FlowObjectiveService;
37 +import org.onosproject.net.flowobjective.ForwardingObjective;
38 +import org.slf4j.Logger;
39 +
40 +import java.util.List;
41 +
42 +import static com.google.common.base.Preconditions.checkArgument;
43 +import static com.google.common.base.Preconditions.checkNotNull;
44 +import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
45 +import static org.slf4j.LoggerFactory.getLogger;
46 +
47 +/**
48 + * Populates rules for virtual tenant network.
49 + */
50 +public final class CordVtnRuleInstaller {
51 + protected final Logger log = getLogger(getClass());
52 +
53 + private static final int DEFAULT_PRIORITY = 5000;
54 +
55 + private final ApplicationId appId;
56 + private final FlowObjectiveService flowObjectiveService;
57 + private final DriverService driverService;
58 + private final String tunnelType;
59 +
60 + /**
61 + * Creates a new rule installer.
62 + *
63 + * @param appId application id
64 + * @param flowObjectiveService flow objective service
65 + * @param driverService driver service
66 + * @param tunnelType tunnel type
67 + */
68 + public CordVtnRuleInstaller(ApplicationId appId,
69 + FlowObjectiveService flowObjectiveService,
70 + DriverService driverService,
71 + String tunnelType) {
72 + this.appId = appId;
73 + this.flowObjectiveService = flowObjectiveService;
74 + this.driverService = driverService;
75 + this.tunnelType = checkNotNull(tunnelType);
76 + }
77 +
78 + /**
79 + * Installs flow rules for tunnel in traffic.
80 + *
81 + * @param deviceId device id to install flow rules
82 + * @param inPort in port
83 + * @param dstInfos list of destination info
84 + */
85 + public void installFlowRulesTunnelIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
86 + dstInfos.stream().forEach(dstInfo -> {
87 + ForwardingObjective.Builder fBuilder = vtnRulesSameNode(inPort, dstInfo);
88 + if (fBuilder != null) {
89 + flowObjectiveService.forward(deviceId, fBuilder.add());
90 + }
91 + });
92 + }
93 +
94 + /**
95 + * Installs flow rules for local in traffic.
96 + *
97 + * @param deviceId device id to install flow rules
98 + * @param inPort in port
99 + * @param dstInfos list of destination info
100 + */
101 + public void installFlowRulesLocalIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
102 + dstInfos.stream().forEach(dstInfo -> {
103 + ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
104 + vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
105 +
106 + if (fBuilder != null) {
107 + flowObjectiveService.forward(deviceId, fBuilder.add());
108 + }
109 + });
110 + }
111 +
112 + /**
113 + * Uninstalls flow rules associated with a given port from a given device.
114 + *
115 + * @param deviceId device id
116 + * @param inPort port associated with removed host
117 + * @param dstInfos list of destination info
118 + */
119 + public void uninstallFlowRules(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
120 + dstInfos.stream().forEach(dstInfo -> {
121 + ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
122 + vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
123 +
124 + if (fBuilder != null) {
125 + flowObjectiveService.forward(deviceId, fBuilder.remove());
126 + }
127 + });
128 + }
129 +
130 + /**
131 + * Returns forwarding objective builder to provision basic virtual tenant network.
132 + * This method cares for the traffics whose source and destination device is the same.
133 + *
134 + * @param inPort in port
135 + * @param dstInfo destination information
136 + * @return forwarding objective builder
137 + */
138 + private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) {
139 + checkArgument(inPort.element().id().equals(dstInfo.output().element().id()));
140 +
141 + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
142 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
143 +
144 + sBuilder.matchInPort(inPort.number())
145 + .matchEthDst(dstInfo.mac());
146 + if (isTunnelPort(inPort)) {
147 + sBuilder.matchTunnelId(dstInfo.tunnelId());
148 + }
149 +
150 + tBuilder.setOutput(dstInfo.output().number());
151 +
152 + return DefaultForwardingObjective.builder()
153 + .withSelector(sBuilder.build())
154 + .withTreatment(tBuilder.build())
155 + .withPriority(DEFAULT_PRIORITY)
156 + .withFlag(ForwardingObjective.Flag.VERSATILE)
157 + .fromApp(appId)
158 + .makePermanent();
159 + }
160 +
161 + /**
162 + * Returns forwarding objective builder to provision basic virtual tenant network.
163 + * This method cares for the traffics whose source and destination is not the same.
164 + *
165 + * @param deviceId device id to install flow rules
166 + * @param inPort in port
167 + * @param dstInfo destination information
168 + * @return forwarding objective, or null if it fails to build it
169 + */
170 + private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) {
171 + checkArgument(isTunnelPort(dstInfo.output()));
172 +
173 + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
174 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
175 +
176 + ExtensionTreatment extTreatment =
177 + getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address());
178 + if (extTreatment == null) {
179 + return null;
180 + }
181 +
182 + sBuilder.matchInPort(inPort.number())
183 + .matchEthDst(dstInfo.mac());
184 +
185 + tBuilder.extension(extTreatment, deviceId)
186 + .setTunnelId(dstInfo.tunnelId())
187 + .setOutput(dstInfo.output().number());
188 +
189 + return DefaultForwardingObjective.builder()
190 + .withSelector(sBuilder.build())
191 + .withTreatment(tBuilder.build())
192 + .withPriority(DEFAULT_PRIORITY)
193 + .withFlag(ForwardingObjective.Flag.VERSATILE)
194 + .fromApp(appId)
195 + .makePermanent();
196 + }
197 +
198 + /**
199 + * Checks if a given port is tunnel interface or not.
200 + * It assumes the tunnel interface contains tunnelType string in its name.
201 + *
202 + * @param port port
203 + * @return true if the port is tunnel interface, false otherwise.
204 + */
205 + private boolean isTunnelPort(Port port) {
206 + return port.annotations().value("portName").contains(tunnelType);
207 + }
208 +
209 + /**
210 + * Returns extension instruction to set tunnel destination.
211 + *
212 + * @param deviceId device id
213 + * @param remoteIp tunnel destination address
214 + * @return extension treatment or null if it fails to get instruction
215 + */
216 + private ExtensionTreatment getTunnelDstInstruction(DeviceId deviceId, Ip4Address remoteIp) {
217 + try {
218 + Driver driver = driverService.getDriver(deviceId);
219 + DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
220 + ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
221 +
222 + ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
223 + treatment.setPropertyValue("tunnelDst", remoteIp);
224 +
225 + return treatment;
226 + } catch (ItemNotFoundException | UnsupportedOperationException | ExtensionPropertyException e) {
227 + log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
228 + return null;
229 + }
230 + }
231 +}
1 +/*
2 + * Copyright 2014-2015 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 + */
16 +package org.onosproject.cordvtn;
17 +
18 +import org.onlab.packet.IpAddress;
19 +import org.onlab.packet.MacAddress;
20 +import org.onosproject.net.Port;
21 +
22 +import java.util.List;
23 +
24 +import static com.google.common.base.Preconditions.checkNotNull;
25 +
26 +/**
27 + * Contains destination information.
28 + */
29 +public final class DestinationInfo {
30 +
31 + private final Port output;
32 + private final List<IpAddress> ip;
33 + private final MacAddress mac;
34 + private final IpAddress remoteIp;
35 + private final long tunnelId;
36 +
37 + /**
38 + * Creates a new destination information.
39 + *
40 + * @param output output port
41 + * @param ip destination ip address
42 + * @param mac destination mac address
43 + * @param remoteIp tunnel remote ip address
44 + * @param tunnelId segment id
45 + */
46 + public DestinationInfo(Port output, List<IpAddress> ip, MacAddress mac,
47 + IpAddress remoteIp, long tunnelId) {
48 + this.output = checkNotNull(output);
49 + this.ip = ip;
50 + this.mac = mac;
51 + this.remoteIp = remoteIp;
52 + this.tunnelId = tunnelId;
53 + }
54 +
55 + /**
56 + * Returns output port.
57 + *
58 + * @return port
59 + */
60 + public Port output() {
61 + return output;
62 + }
63 +
64 + /**
65 + * Returns destination ip addresses.
66 + *
67 + * @return list of ip address
68 + */
69 + public List<IpAddress> ip() {
70 + return ip;
71 + }
72 +
73 + /**
74 + * Returns destination mac address.
75 + *
76 + * @return mac address
77 + */
78 + public MacAddress mac() {
79 + return mac;
80 + }
81 +
82 + /**
83 + * Returns tunnel remote ip address.
84 + *
85 + * @return ip address
86 + */
87 + public IpAddress remoteIp() {
88 + return remoteIp;
89 + }
90 +
91 + /**
92 + * Returns tunnel id.
93 + *
94 + * @return tunnel id
95 + */
96 + public long tunnelId() {
97 + return tunnelId;
98 + }
99 +
100 + /**
101 + * Returns a new destination info builder.
102 + *
103 + * @return destination info builder
104 + */
105 + public static DestinationInfo.Builder builder(Port output) {
106 + return new Builder(output);
107 + }
108 +
109 + /**
110 + * DestinationInfo builder class.
111 + */
112 + public static final class Builder {
113 +
114 + private final Port output;
115 + private List<IpAddress> ip;
116 + private MacAddress mac;
117 + private IpAddress remoteIp;
118 + private long tunnelId;
119 +
120 + /**
121 + * Creates a new destination information builder.
122 + *
123 + * @param output output port
124 + */
125 + public Builder(Port output) {
126 + this.output = checkNotNull(output, "Output port cannot be null");
127 + }
128 +
129 + /**
130 + * Sets the destination ip address.
131 + *
132 + * @param ip ip address
133 + * @return destination info builder
134 + */
135 + public Builder setIp(List<IpAddress> ip) {
136 + this.ip = checkNotNull(ip, "IP cannot be null");
137 + return this;
138 + }
139 +
140 + /**
141 + * Sets the destination mac address.
142 + *
143 + * @param mac mac address
144 + * @return destination info builder
145 + */
146 + public Builder setMac(MacAddress mac) {
147 + this.mac = checkNotNull(mac, "MAC address cannot be null");
148 + return this;
149 + }
150 +
151 + /**
152 + * Sets the tunnel remote ip address.
153 + *
154 + * @param remoteIp ip address
155 + * @return destination info builder
156 + */
157 + public Builder setRemoteIp(IpAddress remoteIp) {
158 + this.remoteIp = checkNotNull(remoteIp, "Remote IP address cannot be null");
159 + return this;
160 + }
161 +
162 + /**
163 + * Sets the tunnel id.
164 + *
165 + * @param tunnelId tunnel id
166 + * @return destination info builder
167 + */
168 + public Builder setTunnelId(long tunnelId) {
169 + this.tunnelId = checkNotNull(tunnelId, "Tunnel ID cannot be null");
170 + return this;
171 + }
172 +
173 + /**
174 + * Build a destination information.
175 + *
176 + * @return destination info object
177 + */
178 + public DestinationInfo build() {
179 + return new DestinationInfo(this);
180 + }
181 + }
182 +
183 + private DestinationInfo(Builder builder) {
184 + output = builder.output;
185 + ip = builder.ip;
186 + mac = builder.mac;
187 + remoteIp = builder.remoteIp;
188 + tunnelId = builder.tunnelId;
189 + }
190 +}