CORD-529, CORD-530 Allow VLAN and floating address pairs for PRIVATE network ports
And deprecated VSG network type
Change-Id: Ib0c1a2b1987463b3a6a4125e5aa7f4451eb51db8
diff --git a/src/main/java/org/opencord/cordvtn/api/core/Instance.java b/src/main/java/org/opencord/cordvtn/api/core/Instance.java
index 24c81b5..a86823f 100644
--- a/src/main/java/org/opencord/cordvtn/api/core/Instance.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/Instance.java
@@ -36,9 +36,8 @@
public static final String NETWORK_ID = "networkId";
public static final String NETWORK_TYPE = "networkType";
public static final String PORT_ID = "portId";
+ public static final String ORIGINAL_HOST_ID = "originalHostId";
public static final String CREATE_TIME = "createTime";
- public static final String NESTED_INSTANCE = "nestedInstance";
- public static final String TRUE = "true";
private final Host host;
@@ -107,12 +106,12 @@
}
/**
- * Returns if the instance is nested container or not.
+ * Returns if the instance is original instance or additional one.
*
- * @return true if it's nested container; false otherwise
+ * @return true if it's additional instance; false otherwise
*/
- public boolean isNestedInstance() {
- return host.annotations().value(NESTED_INSTANCE) != null;
+ public boolean isAdditionalInstance() {
+ return host.annotations().value(ORIGINAL_HOST_ID) != null;
}
/**
diff --git a/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java b/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
index c8eb5ef..596c9de 100644
--- a/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
@@ -35,6 +35,14 @@
void addInstance(ConnectPoint connectPoint);
/**
+ * Adds a host with a given host ID and host description.
+ *
+ * @param hostId host id
+ * @param description host description
+ */
+ void addInstance(HostId hostId, HostDescription description);
+
+ /**
* Removes a service instance from a given connect point.
*
* @param connectPoint connect point
@@ -42,19 +50,9 @@
void removeInstance(ConnectPoint connectPoint);
/**
- * Adds a nested instance with given host ID and host description.
- * Nested instance can be a container inside a virtual machine, for example.
- * DHCP is not supported for the nested instance.
- *
- * @param hostId host id
- * @param description host description
- */
- void addNestedInstance(HostId hostId, HostDescription description);
-
- /**
- * Removes nested instance with a given host ID.
+ * Removes host with host ID.
*
* @param hostId host id
*/
- void removeNestedInstance(HostId hostId);
+ void removeInstance(HostId hostId);
}
diff --git a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
index 849a6ea..36cac9f 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
@@ -32,6 +32,7 @@
PUBLIC,
MANAGEMENT_HOST,
MANAGEMENT_LOCAL,
+ @Deprecated
VSG,
ACCESS_AGENT
}
diff --git a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
index 2011342..b31a575 100644
--- a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
@@ -197,20 +197,8 @@
}
@Override
- public void addNestedInstance(HostId hostId, HostDescription description) {
- DefaultAnnotations annotations = DefaultAnnotations.builder()
- .set(Instance.NESTED_INSTANCE, Instance.TRUE)
- .build();
- annotations = annotations.merge(annotations, description.annotations());
-
- HostDescription nestedHost = new DefaultHostDescription(
- description.hwAddress(),
- description.vlan(),
- description.location(),
- description.ipAddress(),
- annotations);
-
- hostProvider.hostDetected(hostId, nestedHost, false);
+ public void addInstance(HostId hostId, HostDescription description) {
+ hostProvider.hostDetected(hostId, description, false);
}
@Override
@@ -222,7 +210,7 @@
}
@Override
- public void removeNestedInstance(HostId hostId) {
+ public void removeInstance(HostId hostId) {
hostProvider.hostVanished(hostId);
}
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
index 0471ee6..dc6a0d6 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
@@ -101,21 +101,21 @@
}
protected ServiceNetwork getServiceNetwork(Instance instance) {
- ServiceNetwork vtnNet = snetService.serviceNetwork(instance.netId());
- if (vtnNet == null) {
+ ServiceNetwork snet = snetService.serviceNetwork(instance.netId());
+ if (snet == null) {
final String error = String.format(ERR_VTN_NETWORK, instance);
throw new IllegalStateException(error);
}
- return vtnNet;
+ return snet;
}
protected ServicePort getServicePort(Instance instance) {
- ServicePort vtnPort = snetService.servicePort(instance.portId());
- if (vtnPort == null) {
+ ServicePort sport = snetService.servicePort(instance.portId());
+ if (sport == null) {
final String error = String.format(ERR_VTN_PORT, instance);
throw new IllegalStateException(error);
}
- return vtnPort;
+ return sport;
}
private class InternalHostListener implements HostListener {
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
index 1684ce4..0edaaab 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
@@ -16,6 +16,7 @@
package org.opencord.cordvtn.impl.handler;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -24,20 +25,43 @@
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.HostId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
import org.opencord.cordvtn.api.core.Instance;
import org.opencord.cordvtn.api.core.InstanceHandler;
+import org.opencord.cordvtn.api.core.InstanceService;
+import org.opencord.cordvtn.api.net.AddressPair;
import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
import org.opencord.cordvtn.api.node.CordVtnNode;
import org.opencord.cordvtn.impl.CordVtnNodeManager;
import org.opencord.cordvtn.impl.CordVtnPipeline;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
/**
@@ -47,11 +71,17 @@
public class DefaultInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CordVtnPipeline pipeline;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CordVtnNodeManager nodeManager;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstanceService instanceService;
+
@Activate
protected void activate() {
netTypes = ImmutableSet.of(PRIVATE, PUBLIC, VSG);
@@ -65,24 +95,93 @@
@Override
public void instanceDetected(Instance instance) {
- if (instance.isNestedInstance()) {
+ if (instance.isAdditionalInstance()) {
return;
}
- log.info("Instance is detected {}", instance);
+ log.info("Instance is detected or updated {}", instance);
ServiceNetwork snet = getServiceNetwork(instance);
populateDefaultRules(instance, snet, true);
+
+ // TODO handle the case that vlan id is added and then removed
+ ServicePort sport = getServicePort(instance);
+ if (sport.vlanId() != null) {
+ populateVlanRule(
+ instance,
+ sport.vlanId(),
+ nodeManager.dataPort(instance.deviceId()),
+ true);
+ }
+ // FIXME don't add the existing instance again
+ sport.addressPairs().stream().forEach(pair -> {
+ // add instance for the additional address pairs
+ addAdditionalInstance(instance, pair.ip(), pair.mac());
+ });
+ Set<IpAddress> ipAddrs = sport.addressPairs().stream()
+ .map(AddressPair::ip).collect(Collectors.toSet());
+ populateAddressPairRule(instance, ipAddrs, true);
}
@Override
public void instanceRemoved(Instance instance) {
- if (instance.isNestedInstance()) {
+ if (instance.isAdditionalInstance()) {
return;
}
log.info("Instance is removed {}", instance);
ServiceNetwork snet = getServiceNetwork(instance);
populateDefaultRules(instance, snet, false);
+
+ // FIXME service port might be already removed
+ ServicePort sport = getServicePort(instance);
+ if (sport.vlanId() != null) {
+ populateVlanRule(
+ instance,
+ sport.vlanId(),
+ nodeManager.dataPort(instance.deviceId()),
+ false);
+ }
+ boolean isOriginalInstance = !instance.isAdditionalInstance();
+ Set<IpAddress> ipAddrs = sport.addressPairs().stream()
+ .map(AddressPair::ip).collect(Collectors.toSet());
+ populateAddressPairRule(
+ instance,
+ isOriginalInstance ? ImmutableSet.of() : ipAddrs,
+ false);
+ }
+
+ @Override
+ public void instanceUpdated(Instance instance) {
+ if (!instance.isAdditionalInstance()) {
+ ServicePort sport = getServicePort(instance);
+ Set<MacAddress> macAddrs = sport.addressPairs().stream()
+ .map(AddressPair::mac)
+ .collect(Collectors.toSet());
+ hostService.getConnectedHosts(instance.host().location()).stream()
+ .filter(h -> !h.mac().equals(instance.mac()))
+ .filter(h -> !macAddrs.contains(h.mac()))
+ .forEach(h -> instanceService.removeInstance(h.id()));
+ }
+ instanceDetected(instance);
+ }
+
+ private void addAdditionalInstance(Instance instance, IpAddress ip, MacAddress mac) {
+ HostId hostId = HostId.hostId(mac);
+ DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+ .set(Instance.NETWORK_TYPE, instance.netType().toString())
+ .set(Instance.NETWORK_ID, instance.netId().id())
+ .set(Instance.PORT_ID, instance.portId().id())
+ .set(Instance.ORIGINAL_HOST_ID, instance.host().id().toString())
+ .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
+
+ HostDescription hostDesc = new DefaultHostDescription(
+ mac,
+ VlanId.NONE,
+ instance.host().location(),
+ Sets.newHashSet(ip),
+ annotations.build());
+
+ instanceService.addInstance(hostId, hostDesc);
}
private void populateDefaultRules(Instance instance, ServiceNetwork snet, boolean install) {
@@ -277,4 +376,128 @@
pipeline.processFlowRule(install, flowRuleDirect);
});
}
+
+ private void populateVlanRule(Instance instance, VlanId vlanId, PortNumber dataPort,
+ boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(dataPort)
+ .matchVlanId(vlanId)
+ .matchEthDst(instance.mac())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(instance.portNumber())
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
+ .forDevice(instance.deviceId())
+ .forTable(CordVtnPipeline.TABLE_VLAN)
+ .makePermanent()
+ .build();
+
+ pipeline.processFlowRule(install, flowRule);
+
+ selector = DefaultTrafficSelector.builder()
+ .matchInPort(instance.portNumber())
+ .matchVlanId(vlanId)
+ .build();
+
+ treatment = DefaultTrafficTreatment.builder()
+ .setOutput(dataPort)
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
+ .forDevice(instance.deviceId())
+ .forTable(CordVtnPipeline.TABLE_VLAN)
+ .makePermanent()
+ .build();
+
+ pipeline.processFlowRule(install, flowRule);
+ }
+
+ private void populateAddressPairRule(Instance instance, Set<IpAddress> ipAddrs,
+ boolean install) {
+ // for traffic coming from WAN, tag 500 and take through the vSG VM
+ // based on destination ip
+ ipAddrs.stream().forEach(wanIp -> {
+ // for traffic coming from WAN, tag 500 and take through the vSG VM
+ TrafficSelector downstream = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(wanIp.toIpPrefix())
+ .build();
+
+ TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
+ .pushVlan()
+ .setVlanId(CordVtnPipeline.VLAN_WAN)
+ .setEthDst(instance.mac())
+ .setOutput(instance.portNumber())
+ .build();
+
+ FlowRule downstreamFlowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(downstream)
+ .withTreatment(downstreamTreatment)
+ .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
+ .forDevice(instance.deviceId())
+ .forTable(CordVtnPipeline.TABLE_DST)
+ .makePermanent()
+ .build();
+
+ pipeline.processFlowRule(install, downstreamFlowRule);
+ });
+
+ // remove downstream flow rules for the vSG not shown in vsgWanIps
+ for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
+ if (!rule.deviceId().equals(instance.deviceId())) {
+ continue;
+ }
+ PortNumber output = getOutputFromTreatment(rule);
+ if (output == null || !output.equals(instance.portNumber()) ||
+ !isVlanPushFromTreatment(rule)) {
+ continue;
+ }
+
+ IpPrefix dstIp = getDstIpFromSelector(rule);
+ if (dstIp != null && !ipAddrs.contains(dstIp.address())) {
+ pipeline.processFlowRule(false, rule);
+ }
+ }
+ }
+
+ private PortNumber getOutputFromTreatment(FlowRule flowRule) {
+ Instruction instruction = flowRule.treatment().allInstructions().stream()
+ .filter(inst -> inst instanceof Instructions.OutputInstruction)
+ .findFirst()
+ .orElse(null);
+ if (instruction == null) {
+ return null;
+ }
+ return ((Instructions.OutputInstruction) instruction).port();
+ }
+
+ private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
+ Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
+ if (criterion != null && criterion instanceof IPCriterion) {
+ IPCriterion ip = (IPCriterion) criterion;
+ return ip.ip();
+ } else {
+ return null;
+ }
+ }
+
+ private boolean isVlanPushFromTreatment(FlowRule flowRule) {
+ return flowRule.treatment().allInstructions().stream()
+ .filter(inst -> inst instanceof L2ModificationInstruction)
+ .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
+ .findAny()
+ .isPresent();
+ }
}
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
deleted file mode 100644
index f01b0e3..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.opencord.cordvtn.impl.handler;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.host.DefaultHostDescription;
-import org.onosproject.net.host.HostDescription;
-import org.opencord.cordvtn.api.core.Instance;
-import org.opencord.cordvtn.api.core.InstanceHandler;
-import org.opencord.cordvtn.api.core.InstanceService;
-import org.opencord.cordvtn.api.net.AddressPair;
-import org.opencord.cordvtn.api.net.ServicePort;
-import org.opencord.cordvtn.impl.CordVtnNodeManager;
-import org.opencord.cordvtn.impl.CordVtnPipeline;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.VSG;
-
-/**
- * Provides network connectivity for vSG instances.
- */
-@Component(immediate = true)
-public final class VsgInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
-
- private static final String STAG = "stag";
- private static final String VSG_VM = "vsgVm";
- private static final String ERR_VSG_VM = "vSG VM does not exist for %s";
- private static final String MSG_VSG_VM = "vSG VM %s: %s";
- private static final String MSG_VSG_CONTAINER = "vSG container %s: %s";
- private static final String MSG_DETECTED = "detected";
- private static final String MSG_REMOVED = "removed";
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CordVtnPipeline pipeline;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CordVtnNodeManager nodeManager;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowRuleService flowRuleService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected InstanceService instanceService;
-
- @Activate
- protected void activate() {
- netTypes = ImmutableSet.of(VSG);
- super.activate();
- }
-
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- }
-
- @Override
- public void instanceDetected(Instance instance) {
- if (isVsgContainer(instance)) {
- log.info(String.format(MSG_VSG_CONTAINER, MSG_DETECTED, instance));
- Instance vsgVm = getVsgVm(instance);
- if (vsgVm == null) {
- final String error = String.format(ERR_VSG_VM, instance);
- throw new IllegalStateException(error);
- }
-
- ServicePort sport = getServicePort(vsgVm);
- Set<IpAddress> wanIps = sport.addressPairs().stream()
- .map(AddressPair::ip).collect(Collectors.toSet());
- populateVsgRules(
- vsgVm, sport.vlanId(),
- nodeManager.dataPort(vsgVm.deviceId()),
- wanIps, true);
- } else {
- log.info(String.format(MSG_VSG_VM, MSG_DETECTED, instance));
- ServicePort sport = snetService.servicePort(instance.portId());
- if (sport == null || sport.vlanId() == null) {
- // service port can be updated after instance is created
- return;
- }
-
- // insert vSG containers inside the vSG VM as a host
- sport.addressPairs().stream().forEach(pair -> addVsgContainer(
- instance,
- pair.ip(),
- pair.mac(),
- sport.vlanId()));
- }
- }
-
- @Override
- public void instanceUpdated(Instance instance) {
- if (!isVsgContainer(instance)) {
- Set<MacAddress> vsgMacs = getServicePort(instance).addressPairs().stream()
- .map(AddressPair::mac)
- .collect(Collectors.toSet());
- hostService.getConnectedHosts(instance.host().location()).stream()
- .filter(h -> !h.mac().equals(instance.mac()))
- .filter(h -> !vsgMacs.contains(h.mac()))
- .forEach(h -> instanceService.removeNestedInstance(h.id()));
- }
- instanceDetected(instance);
- }
-
- @Override
- public void instanceRemoved(Instance instance) {
- boolean isVsgContainer = isVsgContainer(instance);
- log.info(String.format(
- isVsgContainer ? MSG_VSG_CONTAINER : MSG_VSG_VM,
- MSG_REMOVED, instance));
-
- Instance vsgVm = isVsgContainer ? getVsgVm(instance) : instance;
- if (vsgVm == null) {
- // the rules are removed when VM is removed, do nothing
- return;
- }
-
- // FIXME service port can be removed already
- ServicePort sport = getServicePort(instance);
- Set<IpAddress> wanIps = sport.addressPairs().stream()
- .map(AddressPair::ip).collect(Collectors.toSet());
- populateVsgRules(
- vsgVm, sport.vlanId(),
- nodeManager.dataPort(vsgVm.deviceId()),
- isVsgContainer ? wanIps : ImmutableSet.of(),
- false);
- }
-
- @Override
- protected ServicePort getServicePort(Instance instance) {
- ServicePort sport = snetService.servicePort(instance.portId());
- if (sport == null || sport.vlanId() == null) {
- final String error = String.format(ERR_VTN_PORT, instance);
- throw new IllegalStateException(error);
- }
- return sport;
- }
-
- private boolean isVsgContainer(Instance instance) {
- return !Strings.isNullOrEmpty(instance.getAnnotation(STAG)) &&
- !Strings.isNullOrEmpty(instance.getAnnotation(VSG_VM));
- }
-
- private Instance getVsgVm(Instance vsgContainer) {
- String vsgVmId = vsgContainer.getAnnotation(VSG_VM);
- Host host = hostService.getHost(HostId.hostId(vsgVmId));
- if (host == null) {
- return null;
- }
- return Instance.of(host);
- }
-
- private void addVsgContainer(Instance vsgVm, IpAddress vsgWanIp, MacAddress vsgMac,
- VlanId stag) {
- HostId hostId = HostId.hostId(vsgMac);
- DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
- .set(Instance.NETWORK_TYPE, vsgVm.netType().toString())
- .set(Instance.NETWORK_ID, vsgVm.netId().id())
- .set(Instance.PORT_ID, vsgVm.portId().id())
- .set(STAG, stag.toString())
- .set(VSG_VM, vsgVm.host().id().toString())
- .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
-
- HostDescription hostDesc = new DefaultHostDescription(
- vsgMac,
- VlanId.NONE,
- vsgVm.host().location(),
- Sets.newHashSet(vsgWanIp),
- annotations.build());
-
- instanceService.addNestedInstance(hostId, hostDesc);
- }
-
- private void populateVsgRules(Instance vsgVm, VlanId stag, PortNumber dataPort,
- Set<IpAddress> vsgWanIps, boolean install) {
- // for traffics with s-tag, strip the tag and take through the vSG VM
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchInPort(dataPort)
- .matchVlanId(stag)
- .build();
-
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(vsgVm.portNumber())
- .build();
-
- FlowRule flowRule = DefaultFlowRule.builder()
- .fromApp(appId)
- .withSelector(selector)
- .withTreatment(treatment)
- .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
- .forDevice(vsgVm.deviceId())
- .forTable(CordVtnPipeline.TABLE_VLAN)
- .makePermanent()
- .build();
-
- pipeline.processFlowRule(install, flowRule);
-
- // for traffics with customer vlan, tag with the service vlan based on input port with
- // lower priority to avoid conflict with WAN tag
- selector = DefaultTrafficSelector.builder()
- .matchInPort(vsgVm.portNumber())
- .matchVlanId(stag)
- .build();
-
- treatment = DefaultTrafficTreatment.builder()
- .setOutput(dataPort)
- .build();
-
- flowRule = DefaultFlowRule.builder()
- .fromApp(appId)
- .withSelector(selector)
- .withTreatment(treatment)
- .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
- .forDevice(vsgVm.deviceId())
- .forTable(CordVtnPipeline.TABLE_VLAN)
- .makePermanent()
- .build();
-
- pipeline.processFlowRule(install, flowRule);
-
- // for traffic coming from WAN, tag 500 and take through the vSG VM
- // based on destination ip
- vsgWanIps.stream().forEach(wanIp -> {
- // for traffic coming from WAN, tag 500 and take through the vSG VM
- TrafficSelector downstream = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(wanIp.toIpPrefix())
- .build();
-
- TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
- .pushVlan()
- .setVlanId(CordVtnPipeline.VLAN_WAN)
- .setEthDst(vsgVm.mac())
- .setOutput(vsgVm.portNumber())
- .build();
-
- FlowRule downstreamFlowRule = DefaultFlowRule.builder()
- .fromApp(appId)
- .withSelector(downstream)
- .withTreatment(downstreamTreatment)
- .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
- .forDevice(vsgVm.deviceId())
- .forTable(CordVtnPipeline.TABLE_DST)
- .makePermanent()
- .build();
-
- pipeline.processFlowRule(install, downstreamFlowRule);
- });
-
- // remove downstream flow rules for the vSG not shown in vsgWanIps
- for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
- if (!rule.deviceId().equals(vsgVm.deviceId())) {
- continue;
- }
- PortNumber output = getOutputFromTreatment(rule);
- if (output == null || !output.equals(vsgVm.portNumber()) ||
- !isVlanPushFromTreatment(rule)) {
- continue;
- }
-
- IpPrefix dstIp = getDstIpFromSelector(rule);
- if (dstIp != null && !vsgWanIps.contains(dstIp.address())) {
- pipeline.processFlowRule(false, rule);
- }
- }
- }
-
- private PortNumber getOutputFromTreatment(FlowRule flowRule) {
- Instruction instruction = flowRule.treatment().allInstructions().stream()
- .filter(inst -> inst instanceof Instructions.OutputInstruction)
- .findFirst()
- .orElse(null);
- if (instruction == null) {
- return null;
- }
- return ((Instructions.OutputInstruction) instruction).port();
- }
-
- private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
- Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
- if (criterion != null && criterion instanceof IPCriterion) {
- IPCriterion ip = (IPCriterion) criterion;
- return ip.ip();
- } else {
- return null;
- }
- }
-
- private boolean isVlanPushFromTreatment(FlowRule flowRule) {
- return flowRule.treatment().allInstructions().stream()
- .filter(inst -> inst instanceof L2ModificationInstruction)
- .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
- .findAny()
- .isPresent();
- }
-}