Java tutorial
/* * 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.onosproject.incubator.net.virtual.impl.provider; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Table; 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.Modified; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.incubator.net.virtual.NetworkId; import org.onosproject.incubator.net.virtual.VirtualNetworkService; import org.onosproject.incubator.net.virtual.VirtualPort; import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider; import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm; import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider; import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService; import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.Path; import org.onosproject.net.PortNumber; import org.onosproject.net.device.DeviceService; import org.onosproject.net.flow.CompletedBatchOperation; import org.onosproject.net.flow.DefaultFlowEntry; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleBatchEntry; import org.onosproject.net.flow.FlowRuleBatchOperation; import org.onosproject.net.flow.FlowRuleEvent; import org.onosproject.net.flow.FlowRuleListener; 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.PortCriterion; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.provider.ProviderId; import org.onosproject.net.topology.TopologyService; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import java.util.Dictionary; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableSet.copyOf; import static org.slf4j.LoggerFactory.getLogger; /** * Provider that translate virtual flow rules into physical rules. * Current implementation is based on FlowRules. * This virtualize and de-virtualize virtual flow rules into physical flow rules. * {@link org.onosproject.net.flow.FlowRule} */ @Component(immediate = true) @Service public class DefaultVirtualFlowRuleProvider extends AbstractVirtualProvider implements VirtualFlowRuleProvider { private static final String APP_ID_STR = "org.onosproject.virtual.vnet-flow_"; private final Logger log = getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected TopologyService topologyService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VirtualNetworkService vnService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowRuleService flowRuleService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VirtualProviderRegistryService providerRegistryService; private InternalRoutingAlgorithm internalRoutingAlgorithm; private InternalVirtualFlowRuleManager frm; private ApplicationId appId; private FlowRuleListener flowRuleListener; /** * Creates a provider with the supplied identifier. */ public DefaultVirtualFlowRuleProvider() { super(new ProviderId("vnet-flow", "org.onosproject.virtual.vnet-flow")); } @Activate public void activate() { appId = coreService.registerApplication(APP_ID_STR); providerRegistryService.registerProvider(this); flowRuleListener = new InternalFlowRuleListener(); flowRuleService.addListener(flowRuleListener); internalRoutingAlgorithm = new DefaultInternalRoutingAlgorithm(); frm = new InternalVirtualFlowRuleManager(); log.info("Started"); } @Deactivate public void deactivate() { flowRuleService.removeListener(flowRuleListener); providerRegistryService.unregisterProvider(this); } @Modified protected void modified(ComponentContext context) { Dictionary<?, ?> properties = context.getProperties(); } @Override public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) { for (FlowRule flowRule : flowRules) { devirtualize(networkId, flowRule).forEach(r -> { flowRuleService.applyFlowRules(r); }); } } @Override public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) { for (FlowRule flowRule : flowRules) { devirtualize(networkId, flowRule).forEach(r -> { flowRuleService.removeFlowRules(r); }); } } @Override public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) { checkNotNull(batch); for (FlowRuleBatchEntry fop : batch.getOperations()) { devirtualize(networkId, fop.target()).forEach(f -> flowRuleService.applyFlowRules(f)); } //FIXME: check the success of the all batch operations CompletedBatchOperation status = new CompletedBatchOperation(true, Sets.newConcurrentHashSet(), batch.deviceId()); VirtualFlowRuleProviderService providerService = (VirtualFlowRuleProviderService) providerRegistryService .getProviderService(networkId, VirtualFlowRuleProvider.class); providerService.batchOperationCompleted(batch.id(), status); } public void setEmbeddingAlgorithm(InternalRoutingAlgorithm internalRoutingAlgorithm) { this.internalRoutingAlgorithm = internalRoutingAlgorithm; } /** * Translate the requested physical flow rules into virtual flow rules. * * @param flowRule A virtual flow rule to be translated * @return A flow rule for a specific virtual network */ private FlowRule virtualizeFlowRule(FlowRule flowRule) { return frm.getVirtualRule(flowRule); } private FlowEntry virtualize(FlowEntry flowEntry) { FlowRule vRule = virtualizeFlowRule(flowEntry); FlowEntry vEntry = new DefaultFlowEntry(vRule, flowEntry.state(), flowEntry.life(), flowEntry.packets(), flowEntry.bytes()); return vEntry; } /** * Translate the requested virtual flow rules into physical flow rules. * The translation could be one to many. * * @param flowRule A flow rule from underlying data plane to be translated * @return A set of flow rules for physical network */ private Set<FlowRule> devirtualize(NetworkId networkId, FlowRule flowRule) { Set<FlowRule> outRules = new HashSet<>(); Set<ConnectPoint> ingressPoints = extractIngressPoints(networkId, flowRule.deviceId(), flowRule.selector()); ConnectPoint egressPoint = extractEgressPoints(networkId, flowRule.deviceId(), flowRule.treatment()); if (egressPoint == null) { return outRules; } TrafficSelector.Builder commonSelectorBuilder = DefaultTrafficSelector.builder(); flowRule.selector().criteria().stream().filter(c -> c.type() != Criterion.Type.IN_PORT) .forEach(c -> commonSelectorBuilder.add(c)); TrafficSelector commonSelector = commonSelectorBuilder.build(); TrafficTreatment.Builder commonTreatmentBuilder = DefaultTrafficTreatment.builder(); flowRule.treatment().allInstructions().stream().filter(i -> i.type() != Instruction.Type.OUTPUT) .forEach(i -> commonTreatmentBuilder.add(i)); TrafficTreatment commonTreatment = commonTreatmentBuilder.build(); for (ConnectPoint ingressPoint : ingressPoints) { if (egressPoint.port() == PortNumber.FLOOD) { Set<ConnectPoint> outPoints = vnService.getVirtualPorts(networkId, flowRule.deviceId()).stream() .map(VirtualPort::realizedBy).filter(p -> !p.equals(ingressPoint)) .collect(Collectors.toSet()); for (ConnectPoint outPoint : outPoints) { outRules.addAll(generateRules(networkId, ingressPoint, outPoint, commonSelector, commonTreatment, flowRule)); } } else { outRules.addAll(generateRules(networkId, ingressPoint, egressPoint, commonSelector, commonTreatment, flowRule)); } } return outRules; } /** * Extract ingress connect points of the physical network * from the requested traffic selector. * * @param networkId the virtual network identifier * @param deviceId the virtual device identifier * @param selector the traffic selector to extract ingress point * @return the set of ingress connect points of the physical network */ private Set<ConnectPoint> extractIngressPoints(NetworkId networkId, DeviceId deviceId, TrafficSelector selector) { Set<ConnectPoint> ingressPoints = new HashSet<>(); Set<VirtualPort> vPorts = vnService.getVirtualPorts(networkId, deviceId); PortCriterion portCriterion = ((PortCriterion) selector.getCriterion(Criterion.Type.IN_PORT)); if (portCriterion != null) { PortNumber vInPortNum = portCriterion.port(); Optional<ConnectPoint> optionalCp = vPorts.stream().filter(v -> v.number().equals(vInPortNum)) .map(VirtualPort::realizedBy).findFirst(); if (!optionalCp.isPresent()) { log.warn("Port {} is not realized yet, in Network {}, Device {}", vInPortNum, networkId, deviceId); return ingressPoints; } ingressPoints.add(optionalCp.get()); } else { for (VirtualPort vPort : vPorts) { if (vPort.realizedBy() != null) { ingressPoints.add(vPort.realizedBy()); } else { log.warn("Port {} is not realized yet, in Network {}, " + "Device {}", vPort, networkId, deviceId); } } } return ingressPoints; } /** * Extract egress connect point of the physical network * from the requested traffic treatment. * * @param networkId the virtual network identifier * @param deviceId the virtual device identifier * @param treatment the traffic treatment to extract ingress point * @return the egress connect point of the physical network */ private ConnectPoint extractEgressPoints(NetworkId networkId, DeviceId deviceId, TrafficTreatment treatment) { Set<VirtualPort> vPorts = vnService.getVirtualPorts(networkId, deviceId); PortNumber vOutPortNum = treatment.allInstructions().stream() .filter(i -> i.type() == Instruction.Type.OUTPUT) .map(i -> ((Instructions.OutputInstruction) i).port()).findFirst().get(); Optional<ConnectPoint> optionalCpOut = vPorts.stream().filter(v -> v.number().equals(vOutPortNum)) .map(VirtualPort::realizedBy).findFirst(); if (!optionalCpOut.isPresent()) { if (vOutPortNum.isLogical()) { return new ConnectPoint(DeviceId.deviceId("vNet"), vOutPortNum); } log.warn("Port {} is not realized yet, in Network {}, Device {}", vOutPortNum, networkId, deviceId); return null; } return optionalCpOut.get(); } /** * Generates the corresponding flow rules for the physical network. * * @param networkId The virtual network identifier * @param ingressPoint The ingress point of the physical network * @param egressPoint The egress point of the physical network * @param commonSelector A common traffic selector between the virtual * and physical flow rules * @param commonTreatment A common traffic treatment between the virtual * and physical flow rules * @param flowRule The virtual flow rule to be translated * @return A set of flow rules for the physical network */ private Set<FlowRule> generateRules(NetworkId networkId, ConnectPoint ingressPoint, ConnectPoint egressPoint, TrafficSelector commonSelector, TrafficTreatment commonTreatment, FlowRule flowRule) { Set<FlowRule> outRules = new HashSet<>(); if (ingressPoint.deviceId().equals(egressPoint.deviceId()) || egressPoint.port().isLogical()) { //Traffic is handled inside a single physical switch //No tunnel is needed. TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(commonSelector) .matchInPort(ingressPoint.port()); TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment) .setOutput(egressPoint.port()); FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() .fromApp(vnService.getVirtualNetworkApplicationId(networkId)).forDevice(ingressPoint.deviceId()) .withSelector(selectorBuilder.build()).withTreatment(treatmentBuilder.build()) .withPriority(flowRule.priority()); if (flowRule.isPermanent()) { ruleBuilder.makePermanent(); } else { ruleBuilder.makeTemporary(flowRule.timeout()); } FlowRule rule = ruleBuilder.build(); frm.addIngressRule(flowRule, rule, networkId); outRules.add(rule); } else { //Traffic is handled by multiple physical switches //A tunnel is needed. Path internalPath = internalRoutingAlgorithm.findPath(ingressPoint, egressPoint); checkNotNull(internalPath, "No path between " + ingressPoint.toString() + " " + egressPoint.toString()); ConnectPoint outCp = internalPath.links().get(0).src(); //ingress point of tunnel TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(commonSelector); selectorBuilder.matchInPort(ingressPoint.port()); TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment); //TODO: add the logic to check host location treatmentBuilder.pushVlan().setVlanId(VlanId.vlanId(networkId.id().shortValue())); treatmentBuilder.setOutput(outCp.port()); FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() .fromApp(vnService.getVirtualNetworkApplicationId(networkId)).forDevice(ingressPoint.deviceId()) .withSelector(selectorBuilder.build()).withTreatment(treatmentBuilder.build()) .withPriority(flowRule.priority()); if (flowRule.isPermanent()) { ruleBuilder.makePermanent(); } else { ruleBuilder.makeTemporary(flowRule.timeout()); } FlowRule rule = ruleBuilder.build(); frm.addIngressRule(flowRule, rule, networkId); outRules.add(rule); //routing inside tunnel ConnectPoint inCp = internalPath.links().get(0).dst(); if (internalPath.links().size() > 1) { for (Link l : internalPath.links().subList(1, internalPath.links().size())) { outCp = l.src(); selectorBuilder = DefaultTrafficSelector.builder(commonSelector) .matchVlanId(VlanId.vlanId(networkId.id().shortValue())).matchInPort(inCp.port()); treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment).setOutput(outCp.port()); ruleBuilder = DefaultFlowRule.builder() .fromApp(vnService.getVirtualNetworkApplicationId(networkId)).forDevice(inCp.deviceId()) .withSelector(selectorBuilder.build()).withTreatment(treatmentBuilder.build()) .withPriority(flowRule.priority()); if (flowRule.isPermanent()) { ruleBuilder.makePermanent(); } else { ruleBuilder.makeTemporary(flowRule.timeout()); } outRules.add(ruleBuilder.build()); inCp = l.dst(); } } //egress point of tunnel selectorBuilder = DefaultTrafficSelector.builder(commonSelector) .matchVlanId(VlanId.vlanId(networkId.id().shortValue())).matchInPort(inCp.port()); treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment).popVlan() .setOutput(egressPoint.port()); ruleBuilder = DefaultFlowRule.builder().fromApp(appId).forDevice(egressPoint.deviceId()) .withSelector(selectorBuilder.build()).withTreatment(treatmentBuilder.build()) .withPriority(flowRule.priority()); if (flowRule.isPermanent()) { ruleBuilder.makePermanent(); } else { ruleBuilder.makeTemporary(flowRule.timeout()); } outRules.add(ruleBuilder.build()); } return outRules; } private class InternalFlowRuleListener implements FlowRuleListener { @Override public void event(FlowRuleEvent event) { if ((event.type() == FlowRuleEvent.Type.RULE_ADDED) || (event.type() == FlowRuleEvent.Type.RULE_UPDATED)) { if (frm.isVirtualIngressRule(event.subject())) { NetworkId networkId = frm.getVirtualNetworkId(event.subject()); FlowEntry vEntry = getVirtualFlowEntry(event.subject()); frm.addOrUpdateFlowEntry(networkId, vEntry.deviceId(), vEntry); VirtualFlowRuleProviderService providerService = (VirtualFlowRuleProviderService) providerRegistryService .getProviderService(networkId, VirtualFlowRuleProvider.class); ImmutableList.Builder<FlowEntry> builder = ImmutableList.builder(); builder.addAll(frm.getFlowEntries(networkId, vEntry.deviceId())); providerService.pushFlowMetrics(vEntry.deviceId(), builder.build()); } } else if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) { if (frm.isVirtualIngressRule(event.subject())) { //FIXME confirm all physical rules are removed NetworkId networkId = frm.getVirtualNetworkId(event.subject()); FlowEntry vEntry = getVirtualFlowEntry(event.subject()); VirtualFlowRuleProviderService providerService = (VirtualFlowRuleProviderService) providerRegistryService .getProviderService(networkId, VirtualFlowRuleProvider.class); providerService.flowRemoved(vEntry); } } } private FlowEntry getVirtualFlowEntry(FlowRule rule) { FlowEntry entry = null; for (FlowEntry fe : flowRuleService.getFlowEntries(rule.deviceId())) { if (rule.exactMatch(fe)) { entry = fe; } } FlowRule vRule = virtualize(entry); FlowEntry vEntry = new DefaultFlowEntry(vRule, entry.state(), entry.life(), entry.packets(), entry.bytes()); return vEntry; } } private class InternalVirtualFlowRuleManager { /** <Virtual Network ID, Virtual Device ID, Virtual Flow Rules>.*/ final Table<NetworkId, DeviceId, Set<FlowRule>> flowRuleTable = HashBasedTable.create(); /** <Virtual Network ID, Virtual Device ID, Virtual Flow Entries>.*/ final Table<NetworkId, DeviceId, Set<FlowEntry>> flowEntryTable = HashBasedTable.create(); /** <Physical Flow Rule, Virtual Network ID>.*/ final Map<FlowRule, NetworkId> ingressRuleMap = Maps.newHashMap(); /** <Physical Flow Rule, Virtual Virtual Flow Rule>.*/ final Map<FlowRule, FlowRule> virtualizationMap = Maps.newHashMap(); private Iterable<FlowRule> getFlowRules(NetworkId networkId, DeviceId deviceId) { return flowRuleTable.get(networkId, deviceId); } private Iterable<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId) { return flowEntryTable.get(networkId, deviceId); } private void addFlowRule(NetworkId networkId, DeviceId deviceId, FlowRule flowRule) { Set<FlowRule> set = flowRuleTable.get(networkId, deviceId); if (set == null) { set = Sets.newHashSet(); flowRuleTable.put(networkId, deviceId, set); } set.add(flowRule); } private void removeFlowRule(NetworkId networkId, DeviceId deviceId, FlowRule flowRule) { Set<FlowRule> set = flowRuleTable.get(networkId, deviceId); if (set == null) { return; } set.remove(flowRule); } private void addOrUpdateFlowEntry(NetworkId networkId, DeviceId deviceId, FlowEntry flowEntry) { Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId); if (set == null) { set = Sets.newConcurrentHashSet(); flowEntryTable.put(networkId, deviceId, set); } //Replace old entry with new one set.stream().filter(fe -> fe.exactMatch(flowEntry)).forEach(set::remove); set.add(flowEntry); } private void removeFlowEntry(NetworkId networkId, DeviceId deviceId, FlowEntry flowEntry) { Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId); if (set == null) { return; } set.remove(flowEntry); } private void addIngressRule(FlowRule virtualRule, FlowRule physicalRule, NetworkId networkId) { ingressRuleMap.put(physicalRule, networkId); virtualizationMap.put(physicalRule, virtualRule); } private FlowRule getVirtualRule(FlowRule physicalRule) { return virtualizationMap.get(physicalRule); } private void removeIngressRule(FlowRule physicalRule) { ingressRuleMap.remove(physicalRule); virtualizationMap.remove(physicalRule); } private Set<FlowRule> getAllPhysicalRule() { return copyOf(virtualizationMap.keySet()); } private NetworkId getVirtualNetworkId(FlowRule physicalRule) { return ingressRuleMap.get(physicalRule); } /** * Test the rule is the ingress rule for virtual rules. * * @param flowRule A flow rule from underlying data plane to be translated * @return True when the rule is for ingress point for a virtual switch */ private boolean isVirtualIngressRule(FlowRule flowRule) { return ingressRuleMap.containsKey(flowRule); } } private class DefaultInternalRoutingAlgorithm implements InternalRoutingAlgorithm { @Override public Path findPath(ConnectPoint src, ConnectPoint dst) { Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(), src.deviceId(), dst.deviceId()); if (paths.isEmpty()) { return null; } //TODO the logic find the best path return (Path) paths.toArray()[0]; } } }