In order to implement VOLTHA VOL-949 task (https://jira.opencord.org/browse/VOL-949) , OltPipeline (pmc-olt driver) must support meter, transition and also QinQ vlan push instructions.
Change-Id: Ie027469fa190a78b0c8366da55431ebc62e699ef
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java
index 26462ad..f88be70 100644
--- a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java
@@ -195,6 +195,8 @@
break;
case METER_REMOVED:
break;
+ case METER_REFERENCE_COUNT_ZERO:
+ break;
default:
log.warn("Unknown meter event {}", event.type());
}
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterEvent.java b/core/api/src/main/java/org/onosproject/net/meter/MeterEvent.java
index fe4fb7b..a380f2d 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterEvent.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterEvent.java
@@ -42,7 +42,12 @@
/**
* A meter was finally removed from device.
*/
- METER_REMOVED
+ METER_REMOVED,
+
+ /**
+ * Meter is not used by any flow. It can be deleted.
+ */
+ METER_REFERENCE_COUNT_ZERO
}
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
index 11420a9..f6f4816 100644
--- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
@@ -375,6 +375,10 @@
log.info("Meter removed {}", event.subject());
post(new MeterEvent(MeterEvent.Type.METER_REMOVED, event.subject()));
break;
+ case METER_REFERENCE_COUNT_ZERO:
+ log.info("Meter reference count zero {}", event.subject());
+ post(new MeterEvent(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO, event.subject()));
+ break;
default:
log.warn("Unknown meter event {}", event.type());
}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java b/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
index 09a55ad..1b5c9ca 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
@@ -285,6 +285,9 @@
m.setLife(meter.life());
// TODO: Prune if drops to zero.
m.setReferenceCount(meter.referenceCount());
+ if (meter.referenceCount() == 0) {
+ notifyDelegate(new MeterEvent(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO, m));
+ }
return new MeterData(m, null, v.origin());
});
}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
index 73eaa8b..6580e31 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
@@ -19,6 +19,7 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -82,6 +83,8 @@
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import java.util.Arrays;
+import java.util.Objects;
import static org.slf4j.LoggerFactory.getLogger;
@@ -94,6 +97,7 @@
private static final Integer QQ_TABLE = 1;
private static final short MCAST_VLAN = 4000;
private static final String OLTCOOKIES = "olt-cookies-must-be-unique";
+ private static final int NO_ACTION_PRIORITY = 500;
private final Logger log = getLogger(getClass());
private ServiceDirectory serviceDirectory;
@@ -231,7 +235,7 @@
List<Instruction> instructions = treatment.allInstructions();
- Optional<Instruction> vlanIntruction = instructions.stream()
+ Optional<Instruction> vlanInstruction = instructions.stream()
.filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
.filter(i -> ((L2ModificationInstruction) i).subtype() ==
L2ModificationInstruction.L2SubType.VLAN_PUSH ||
@@ -239,12 +243,12 @@
L2ModificationInstruction.L2SubType.VLAN_POP)
.findAny();
- if (!vlanIntruction.isPresent()) {
+
+ if (!vlanInstruction.isPresent()) {
installNoModificationRules(fwd);
} else {
L2ModificationInstruction vlanIns =
- (L2ModificationInstruction) vlanIntruction.get();
-
+ (L2ModificationInstruction) vlanInstruction.get();
if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
installUpstreamRules(fwd);
} else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
@@ -409,62 +413,114 @@
}
private void installDownstreamRules(ForwardingObjective fwd) {
- List<Pair<Instruction, Instruction>> vlanOps =
- vlanOps(fwd,
- L2ModificationInstruction.L2SubType.VLAN_POP);
-
- if (vlanOps == null) {
- return;
- }
-
Instructions.OutputInstruction output = (Instructions.OutputInstruction) fetchOutput(fwd, "downstream");
if (output == null) {
return;
}
- Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
-
TrafficSelector selector = fwd.selector();
Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
- Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
+ Criterion innerVlanCriterion = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
- long cvid = ((VlanIdCriterion) innerVlan).vlanId().toShort();
- long outPort = output.port().toLong() & 0x0FFFFFFFFL;
-
- Criterion metadata = Criteria.matchMetadata((cvid << 32) | outPort);
-
- if (outerVlan == null || inport == null) {
+ if (outerVlan == null || innerVlanCriterion == null || inport == null) {
log.error("Forwarding objective is underspecified: {}", fwd);
fail(fwd, ObjectiveError.BADPARAMS);
return;
}
- Criterion innerVid = Criteria.matchVlanId(((VlanIdCriterion) innerVlan).vlanId());
+ VlanId innerVlan = ((VlanIdCriterion) innerVlanCriterion).vlanId();
+ Criterion innerVid = Criteria.matchVlanId(innerVlan);
+ long cvid = innerVlan.toShort();
+ long outPort = output.port().toLong() & 0x0FFFFFFFFL;
+ Criterion metadata = Criteria.matchMetadata((cvid << 32) | outPort);
+
+ TrafficSelector outerSelector = buildSelector(inport, outerVlan, metadata);
+
+ if (innerVlan.toShort() == VlanId.ANY_VALUE) {
+ installDownstreamRulesForAnyVlan(fwd, output, outerSelector, buildSelector(inport,
+ Criteria.matchVlanId(VlanId.ANY)));
+ } else {
+ installDownstreamRulesForVlans(fwd, output, outerSelector, buildSelector(inport, innerVid));
+ }
+ }
+
+ private void installDownstreamRulesForVlans(ForwardingObjective fwd, Instruction output,
+ TrafficSelector outerSelector, TrafficSelector innerSelector) {
+
+ List<Pair<Instruction, Instruction>> vlanOps =
+ vlanOps(fwd,
+ L2ModificationInstruction.L2SubType.VLAN_POP);
+
+ if (vlanOps == null || vlanOps.isEmpty()) {
+ return;
+ }
+
+ Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
+
+ TrafficTreatment innerTreatment;
+ VlanId setVlanId = ((L2ModificationInstruction.ModVlanIdInstruction) popAndRewrite.getRight()).vlanId();
+ if (VlanId.NONE.equals(setVlanId)) {
+ innerTreatment = (buildTreatment(true, output, popAndRewrite.getLeft(), fetchMeter(fwd),
+ fetchTransition(fwd)));
+ } else {
+ innerTreatment = (buildTreatment(true, output, popAndRewrite.getLeft(), popAndRewrite.getRight(),
+ fetchMeter(fwd), fetchTransition(fwd)));
+ }
+
+ //match: in port (nni), s-tag, metadata
+ //action: pop vlan (s-tag), go to table 1
FlowRule.Builder outer = DefaultFlowRule.builder()
.fromApp(fwd.appId())
.forDevice(deviceId)
.makePermanent()
.withPriority(fwd.priority())
- .withSelector(buildSelector(inport, outerVlan, metadata))
+ .withSelector(outerSelector)
.withTreatment(buildTreatment(popAndRewrite.getLeft(),
- Instructions.transition(QQ_TABLE)));
+ Instructions.transition(QQ_TABLE)));
+ //match: in port (nni), c-tag
+ //action: deferred: output, immediate: pop, meter, go to 64 - 127 (technology profile)
FlowRule.Builder inner = DefaultFlowRule.builder()
.fromApp(fwd.appId())
.forDevice(deviceId)
.forTable(QQ_TABLE)
.makePermanent()
.withPriority(fwd.priority())
- .withSelector(buildSelector(inport, innerVid))
- .withTreatment(buildTreatment(popAndRewrite.getLeft(),
- output));
+ .withSelector(innerSelector)
+ .withTreatment(innerTreatment);
+ applyRules(fwd, inner, outer);
+ }
+
+ private void installDownstreamRulesForAnyVlan(ForwardingObjective fwd, Instruction output,
+ TrafficSelector outerSelector, TrafficSelector innerSelector) {
+
+ //match: in port (nni), s-tag and metadata
+ //action: deferred: pop vlan (s-tag), immediate: go to table 1
+ FlowRule.Builder outer = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .forDevice(deviceId)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(outerSelector)
+ .withTreatment(buildTreatment(true, Instructions.popVlan(),
+ Instructions.transition(QQ_TABLE)));
+
+ //match: in port (nni) and s-tag
+ //action: deferred: output, immediate : none, meter, go to 64 - 127 (technology profile)
+ FlowRule.Builder inner = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .forDevice(deviceId)
+ .forTable(QQ_TABLE)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(innerSelector)
+ .withTreatment(buildTreatment(true, output, fetchMeter(fwd), fetchTransition(fwd)));
applyRules(fwd, inner, outer);
-
}
private void installUpstreamRules(ForwardingObjective fwd) {
@@ -472,7 +528,7 @@
vlanOps(fwd,
L2ModificationInstruction.L2SubType.VLAN_PUSH);
- if (vlanOps == null) {
+ if (vlanOps == null || vlanOps.isEmpty()) {
return;
}
@@ -483,31 +539,41 @@
}
Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
-
Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
- // Add the VLAN_PUSH treatment if we're matching on VlanId.NONE
- Criterion vlanMatchCriterion = filterForCriterion(fwd.selector().criteria(), Criterion.Type.VLAN_VID);
- boolean push = false;
- if (vlanMatchCriterion != null) {
- push = ((VlanIdCriterion) vlanMatchCriterion).vlanId().equals(VlanId.NONE);
- }
+ boolean noneValueVlanStatus = checkNoneVlanCriteria(fwd);
+ boolean anyValueVlanStatus = checkAnyVlanMatchCriteria(fwd);
- TrafficTreatment treatment;
- if (push) {
- treatment = buildTreatment(innerPair.getLeft(), innerPair.getRight(),
+ if (anyValueVlanStatus) {
+ installUpstreamRulesForAnyVlan(fwd, output, outerPair);
+ } else {
+ installUpstreamRulesForVlans(fwd, output, innerPair, outerPair, noneValueVlanStatus);
+ }
+ }
+
+ private void installUpstreamRulesForVlans(ForwardingObjective fwd, Instruction output,
+ Pair<Instruction, Instruction> innerPair,
+ Pair<Instruction, Instruction> outerPair, Boolean noneValueVlanStatus) {
+
+ TrafficTreatment innerTreatment;
+ if (noneValueVlanStatus) {
+ innerTreatment = buildTreatment(innerPair.getLeft(), innerPair.getRight(),
Instructions.transition(QQ_TABLE));
} else {
- treatment = buildTreatment(innerPair.getRight(), Instructions.transition(QQ_TABLE));
+ innerTreatment = buildTreatment(innerPair.getRight(), Instructions.transition(QQ_TABLE));
}
+ //match: in port, vlanId (0 or None)
+ //action:
+ //if vlanId None, push & set c-tag go to table 1
+ //if vlanId 0 or any specific vlan, set c-tag go to table 1
FlowRule.Builder inner = DefaultFlowRule.builder()
.fromApp(fwd.appId())
.forDevice(deviceId)
.makePermanent()
.withPriority(fwd.priority())
.withSelector(fwd.selector())
- .withTreatment(treatment);
+ .withTreatment(innerTreatment);
PortCriterion inPort = (PortCriterion)
fwd.selector().getCriterion(Criterion.Type.IN_PORT);
@@ -515,20 +581,91 @@
VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
innerPair.getRight()).vlanId();
+ //match: in port, c-tag
+ //action: deferred: output, immediate: push s-tag, meter, go to 64 - 127 (technology profile id)
FlowRule.Builder outer = DefaultFlowRule.builder()
.fromApp(fwd.appId())
.forDevice(deviceId)
.forTable(QQ_TABLE)
.makePermanent()
.withPriority(fwd.priority())
- .withSelector(buildSelector(inPort,
- Criteria.matchVlanId(cVlanId)))
- .withTreatment(buildTreatment(outerPair.getLeft(),
- outerPair.getRight(),
- output));
+ .withSelector(buildSelector(inPort, Criteria.matchVlanId(cVlanId)))
+ .withTreatment(buildTreatment(true, output, outerPair.getLeft(), outerPair.getRight(),
+ fetchMeter(fwd), fetchTransition(fwd)));
applyRules(fwd, inner, outer);
+ }
+ private void installUpstreamRulesForAnyVlan(ForwardingObjective fwd, Instruction output,
+ Pair<Instruction, Instruction> outerPair) {
+
+ log.debug("Installing upstream rules for any value vlan");
+
+ //match: in port and any-vlan (coming from OLT app.)
+ //action: go to table 1
+ FlowRule.Builder inner = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .forDevice(deviceId)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(fwd.selector())
+ .withTreatment(buildTreatment(Instructions.transition(QQ_TABLE)));
+
+
+ TrafficSelector defaultSelector = DefaultTrafficSelector.builder()
+ .matchInPort(((PortCriterion) fwd.selector().getCriterion(Criterion.Type.IN_PORT)).port())
+ .build();
+
+ //drop the packets that don't have vlan
+ //match: in port
+ //action: no action
+ FlowRule.Builder defaultInner = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .forDevice(deviceId)
+ .makePermanent()
+ .withPriority(NO_ACTION_PRIORITY)
+ .withSelector(defaultSelector)
+ .withTreatment(DefaultTrafficTreatment.emptyTreatment());
+
+ Instruction qinqInstruction = Instructions.pushVlan(EthType.EtherType.QINQ.ethType());
+
+ //match: in port and any-vlan (coming from OLT app.)
+ //action: deferred: output, immediate: push:QinQ, vlanId (s-tag), meter, go to 64-127 (technology profile id)
+ FlowRule.Builder outer = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .forDevice(deviceId)
+ .forTable(QQ_TABLE)
+ .makePermanent()
+ .withPriority(fwd.priority())
+ .withSelector(fwd.selector())
+ .withTreatment(buildTreatment(true, output, qinqInstruction, outerPair.getRight(),
+ fetchMeter(fwd), fetchTransition(fwd)));
+
+ applyRules(fwd, inner, defaultInner, outer);
+ }
+
+ private boolean checkNoneVlanCriteria(ForwardingObjective fwd) {
+ // Add the VLAN_PUSH treatment if we're matching on VlanId.NONE
+ Criterion vlanMatchCriterion = filterForCriterion(fwd.selector().criteria(), Criterion.Type.VLAN_VID);
+ boolean noneValueVlanStatus = false;
+ if (vlanMatchCriterion != null) {
+ noneValueVlanStatus = ((VlanIdCriterion) vlanMatchCriterion).vlanId().equals(VlanId.NONE);
+ }
+ return noneValueVlanStatus;
+ }
+
+ private boolean checkAnyVlanMatchCriteria(ForwardingObjective fwd) {
+ Criterion anyValueVlanCriterion = fwd.selector().criteria().stream()
+ .filter(c -> c.type().equals(Criterion.Type.VLAN_VID))
+ .filter(vc -> ((VlanIdCriterion) vc).vlanId().toShort() == VlanId.ANY_VALUE)
+ .findAny().orElse(null);
+
+ if (anyValueVlanCriterion == null) {
+ log.debug("Any value vlan match criteria is not found");
+ return false;
+ }
+
+ return true;
}
private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
@@ -544,18 +681,42 @@
return output;
}
+ private Instruction fetchMeter(ForwardingObjective fwd) {
+ Instruction meter = fwd.treatment().metered();
+
+ if (meter == null) {
+ log.debug("Meter instruction is not found for the forwarding objective {}", fwd);
+ return null;
+ }
+
+ log.debug("Meter instruction is found.");
+ return meter;
+ }
+
+ private Instruction fetchTransition(ForwardingObjective fwd) {
+ Instruction transition = fwd.treatment().tableTransition();
+
+ if (transition == null) {
+ log.debug("Table / transition instruction is not found for the forwarding objective {}", fwd);
+ return null;
+ }
+
+ log.debug("Transition instruction is found.");
+ return transition;
+ }
+
private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
L2ModificationInstruction.L2SubType type) {
List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
fwd.treatment().allInstructions(), type);
- if (vlanOps == null) {
+ if (vlanOps == null || vlanOps.isEmpty()) {
String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
? "downstream" : "upstream";
log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
fail(fwd, ObjectiveError.BADPARAMS);
- return null;
+ return ImmutableList.of();
}
return vlanOps;
}
@@ -572,7 +733,7 @@
instructions);
if (vlanPushs.size() != vlanSets.size()) {
- return null;
+ return ImmutableList.of();
}
List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
@@ -618,6 +779,7 @@
TrafficTreatment treatment = buildTreatment(output);
buildAndApplyRule(filter, selector, treatment);
}
+
private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
TrafficTreatment treatment) {
FlowRule rule = DefaultFlowRule.builder()
@@ -647,36 +809,18 @@
applyFlowRules(opsBuilder, filter);
}
- private void applyRules(ForwardingObjective fwd,
- FlowRule.Builder outer) {
+ private void applyRules(ForwardingObjective fwd, FlowRule.Builder... fwdBuilders) {
FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
switch (fwd.op()) {
case ADD:
- builder.add(outer.build());
+ for (FlowRule.Builder fwdBuilder : fwdBuilders) {
+ builder.add(fwdBuilder.build());
+ }
break;
case REMOVE:
- builder.remove(outer.build());
- break;
- case ADD_TO_EXISTING:
- break;
- case REMOVE_FROM_EXISTING:
- break;
- default:
- log.warn("Unknown forwarding operation: {}", fwd.op());
- }
-
- applyFlowRules(builder, fwd);
- }
-
- private void applyRules(ForwardingObjective fwd,
- FlowRule.Builder inner, FlowRule.Builder outer) {
- FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
- switch (fwd.op()) {
- case ADD:
- builder.add(inner.build()).add(outer.build());
- break;
- case REMOVE:
- builder.remove(inner.build()).remove(outer.build());
+ for (FlowRule.Builder fwdBuilder : fwdBuilders) {
+ builder.remove(fwdBuilder.build());
+ }
break;
case ADD_TO_EXISTING:
break;
@@ -723,6 +867,20 @@
return sBuilder.build();
}
+ private TrafficTreatment buildTreatment(boolean isDeferred, Instruction deferredInst, Instruction... instructions) {
+
+ TrafficTreatment.Builder dBuilder = DefaultTrafficTreatment.builder();
+
+ Arrays.stream(instructions).filter(Objects::nonNull).forEach(treatment -> dBuilder.add(treatment));
+
+ if (isDeferred) {
+ dBuilder.deferred();
+ dBuilder.add(deferredInst);
+ }
+
+ return dBuilder.build();
+ }
+
private TrafficTreatment buildTreatment(Instruction... instructions) {
@@ -749,7 +907,7 @@
@Override
public void event(GroupEvent event) {
if (event.type() == GroupEvent.Type.GROUP_ADDED ||
- event.type() == GroupEvent.Type.GROUP_UPDATED) {
+ event.type() == GroupEvent.Type.GROUP_UPDATED) {
GroupKey key = event.subject().appCookie();
NextObjective obj = pendingGroups.getIfPresent(key);