Refactor: split api from SONA simple fabric
Change-Id: Icbdb10b730af29057097b14df8ddc377f4107853
diff --git a/apps/simplefabric/app/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java b/apps/simplefabric/app/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java
new file mode 100644
index 0000000..45a9fdd
--- /dev/null
+++ b/apps/simplefabric/app/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.simplefabric;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+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.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.simplefabric.api.L2Network;
+import org.onosproject.simplefabric.api.SimpleFabricEvent;
+import org.onosproject.simplefabric.api.SimpleFabricListener;
+import org.onosproject.simplefabric.api.SimpleFabricService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * An implementation of L2NetworkOperationService.
+ * Handles the execution order of the L2 Network operations generated by the
+ * application.
+ */
+@Component(immediate = true, enabled = false)
+public class SimpleFabricL2Forward {
+
+ public static final String BROADCAST = "BCAST";
+ public static final String UNICAST = "UNI";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ protected ApplicationId l2ForwardAppId;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected SimpleFabricService simpleFabric;
+
+ public static final ImmutableList<Constraint> L2NETWORK_CONSTRAINTS =
+ ImmutableList.of(new PartialFailureConstraint());
+
+ private Map<Key, SinglePointToMultiPointIntent> bctIntentsMap = Maps.newConcurrentMap();
+ private Map<Key, MultiPointToSinglePointIntent> uniIntentsMap = Maps.newConcurrentMap();
+ private Set<Key> toBePurgedIntentKeys = new HashSet<>();
+
+ private final InternalSimpleFabricListener simpleFabricListener = new InternalSimpleFabricListener();
+
+ @Activate
+ public void activate() {
+ l2ForwardAppId = coreService.registerApplication(simpleFabric.L2FORWARD_APP_ID);
+ log.info("simple fabric l2 forwaring starting with l2net app id {}", l2ForwardAppId.toString());
+
+ simpleFabric.addListener(simpleFabricListener);
+
+ refresh();
+ checkIntentsPurge();
+
+ log.info("simple fabric l2forward started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("simple fabric l2forward stopping");
+
+ simpleFabric.removeListener(simpleFabricListener);
+
+ for (Intent intent : bctIntentsMap.values()) {
+ intentService.withdraw(intent);
+ toBePurgedIntentKeys.add(intent.key());
+ }
+ for (Intent intent : uniIntentsMap.values()) {
+ intentService.withdraw(intent);
+ toBePurgedIntentKeys.add(intent.key());
+ }
+ for (Key key : toBePurgedIntentKeys) {
+ Intent intentToPurge = intentService.getIntent(key);
+ if (intentToPurge != null) {
+ intentService.purge(intentToPurge);
+ }
+ }
+
+ // do not set clear for switch compatibility
+ //bctIntentsMap.clear();
+ //uniIntentsMap.clear();
+
+ log.info("simple fabric l2forward stopped");
+ }
+
+ private void refresh() {
+ log.debug("simple fabric l2forward refresh");
+
+ Map<Key, SinglePointToMultiPointIntent> newBctIntentsMap = Maps.newConcurrentMap();
+ Map<Key, MultiPointToSinglePointIntent> newUniIntentsMap = Maps.newConcurrentMap();
+
+ for (L2Network l2Network : simpleFabric.getL2Networks()) {
+ // scans all l2network regardless of dirty flag
+ // if l2Network.l2Forward == false or number of interfaces() < 2, no Intents generated
+ for (SinglePointToMultiPointIntent intent : buildBrcIntents(l2Network)) {
+ newBctIntentsMap.put(intent.key(), intent);
+ }
+ for (MultiPointToSinglePointIntent intent : buildUniIntents(l2Network, hostsFromL2Network(l2Network))) {
+ newUniIntentsMap.put(intent.key(), intent);
+ }
+ if (l2Network.dirty()) {
+ l2Network.setDirty(false);
+ }
+ }
+
+ boolean bctUpdated = false;
+ for (SinglePointToMultiPointIntent intent : bctIntentsMap.values()) {
+ SinglePointToMultiPointIntent newIntent = newBctIntentsMap.get(intent.key());
+ if (newIntent == null) {
+ log.info("simple fabric l2forward withdraw broadcast intent: {}", intent.key().toString());
+ toBePurgedIntentKeys.add(intent.key());
+ intentService.withdraw(intent);
+ bctUpdated = true;
+ }
+ }
+ for (SinglePointToMultiPointIntent intent : newBctIntentsMap.values()) {
+ SinglePointToMultiPointIntent oldIntent = bctIntentsMap.get(intent.key());
+ if (oldIntent == null ||
+ !oldIntent.filteredEgressPoints().equals(intent.filteredEgressPoints()) ||
+ !oldIntent.filteredIngressPoint().equals(intent.filteredIngressPoint()) ||
+ !oldIntent.selector().equals(intent.selector()) ||
+ !oldIntent.treatment().equals(intent.treatment()) ||
+ !oldIntent.constraints().equals(intent.constraints())) {
+ log.info("simple fabric l2forward submit broadcast intent: {}", intent.key().toString());
+ toBePurgedIntentKeys.remove(intent.key());
+ intentService.submit(intent);
+ bctUpdated = true;
+ }
+ }
+
+ boolean uniUpdated = false;
+ for (MultiPointToSinglePointIntent intent : uniIntentsMap.values()) {
+ MultiPointToSinglePointIntent newIntent = newUniIntentsMap.get(intent.key());
+ if (newIntent == null) {
+ log.info("simple fabric l2forward withdraw unicast intent: {}", intent.key().toString());
+ toBePurgedIntentKeys.add(intent.key());
+ intentService.withdraw(intent);
+ uniUpdated = true;
+ }
+ }
+ for (MultiPointToSinglePointIntent intent : newUniIntentsMap.values()) {
+ MultiPointToSinglePointIntent oldIntent = uniIntentsMap.get(intent.key());
+ if (oldIntent == null ||
+ !oldIntent.filteredEgressPoint().equals(intent.filteredEgressPoint()) ||
+ !oldIntent.filteredIngressPoints().equals(intent.filteredIngressPoints()) ||
+ !oldIntent.selector().equals(intent.selector()) ||
+ !oldIntent.treatment().equals(intent.treatment()) ||
+ !oldIntent.constraints().equals(intent.constraints())) {
+ log.info("simple fabric l2forward submit unicast intent: {}", intent.key().toString());
+ toBePurgedIntentKeys.remove(intent.key());
+ intentService.submit(intent);
+ uniUpdated = true;
+ }
+ }
+
+ if (bctUpdated) {
+ bctIntentsMap = newBctIntentsMap;
+ }
+ if (uniUpdated) {
+ uniIntentsMap = newUniIntentsMap;
+ }
+ }
+
+ private void checkIntentsPurge() {
+ // check intents to be purge
+ if (!toBePurgedIntentKeys.isEmpty()) {
+ Set<Key> purgedKeys = new HashSet<>();
+ for (Key key : toBePurgedIntentKeys) {
+ Intent intentToPurge = intentService.getIntent(key);
+ if (intentToPurge == null) {
+ log.info("simple fabric l2forward purged intent: key={}", key.toString());
+ purgedKeys.add(key);
+ } else {
+ switch (intentService.getIntentState(key)) {
+ case FAILED:
+ case WITHDRAWN:
+ log.info("simple fabric l2forward try to purge intent: key={}", key.toString());
+ intentService.purge(intentToPurge);
+ break;
+ case INSTALL_REQ:
+ case INSTALLED:
+ case INSTALLING:
+ case RECOMPILING:
+ case COMPILING:
+ log.warn("simple fabric l2forward withdraw intent to purge: key={}", key);
+ intentService.withdraw(intentToPurge);
+ break;
+ case WITHDRAW_REQ:
+ case WITHDRAWING:
+ case PURGE_REQ:
+ case CORRUPT:
+ default:
+ // no action
+ break;
+ }
+ }
+ }
+ toBePurgedIntentKeys.removeAll(purgedKeys);
+ }
+ }
+
+ // Generates Unicast Intents and broadcast Intents for the L2 Network.
+
+ private Set<Intent> generateL2NetworkIntents(L2Network l2Network) {
+ return new ImmutableSet.Builder<Intent>()
+ .addAll(buildBrcIntents(l2Network))
+ .addAll(buildUniIntents(l2Network, hostsFromL2Network(l2Network)))
+ .build();
+ }
+
+ // Build Boadcast Intents for a L2 Network.
+ private Set<SinglePointToMultiPointIntent> buildBrcIntents(L2Network l2Network) {
+ Set<Interface> interfaces = l2Network.interfaces();
+ if (interfaces.size() < 2 || !l2Network.l2Forward() || !l2Network.l2Broadcast()) {
+ return ImmutableSet.of();
+ }
+ Set<SinglePointToMultiPointIntent> brcIntents = Sets.newHashSet();
+ ResourceGroup resourceGroup = ResourceGroup.of(l2Network.name());
+
+ // Generates broadcast Intents from any network interface to other
+ // network interface from the L2 Network.
+ interfaces
+ .forEach(src -> {
+ FilteredConnectPoint srcFcp = buildFilteredConnectedPoint(src);
+ Set<FilteredConnectPoint> dstFcps = interfaces.stream()
+ .filter(iface -> !iface.equals(src))
+ .map(this::buildFilteredConnectedPoint)
+ .collect(Collectors.toSet());
+ Key key = buildKey(l2Network.name(), "BCAST", srcFcp.connectPoint(), MacAddress.BROADCAST);
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthDst(MacAddress.BROADCAST)
+ .build();
+ SinglePointToMultiPointIntent.Builder intentBuilder = SinglePointToMultiPointIntent.builder()
+ .appId(l2ForwardAppId)
+ .key(key)
+ .selector(selector)
+ .filteredIngressPoint(srcFcp)
+ .filteredEgressPoints(dstFcps)
+ .constraints(buildConstraints(L2NETWORK_CONSTRAINTS, l2Network.encapsulation()))
+ .priority(SimpleFabricService.PRI_L2NETWORK_BROADCAST)
+ .resourceGroup(resourceGroup);
+ brcIntents.add(intentBuilder.build());
+ });
+ return brcIntents;
+ }
+
+ // Builds unicast Intents for a L2 Network.
+ private Set<MultiPointToSinglePointIntent> buildUniIntents(L2Network l2Network, Set<Host> hosts) {
+ Set<Interface> interfaces = l2Network.interfaces();
+ if (!l2Network.l2Forward() || interfaces.size() < 2) {
+ return ImmutableSet.of();
+ }
+ Set<MultiPointToSinglePointIntent> uniIntents = Sets.newHashSet();
+ ResourceGroup resourceGroup = ResourceGroup.of(l2Network.name());
+ hosts.forEach(host -> {
+ FilteredConnectPoint hostFcp = buildFilteredConnectedPoint(host);
+ Set<FilteredConnectPoint> srcFcps = interfaces.stream()
+ .map(this::buildFilteredConnectedPoint)
+ .filter(fcp -> !fcp.equals(hostFcp))
+ .collect(Collectors.toSet());
+ Key key = buildKey(l2Network.name(), "UNI", hostFcp.connectPoint(), host.mac());
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthDst(host.mac()).build();
+ MultiPointToSinglePointIntent.Builder intentBuilder = MultiPointToSinglePointIntent.builder()
+ .appId(l2ForwardAppId)
+ .key(key)
+ .selector(selector)
+ .filteredIngressPoints(srcFcps)
+ .filteredEgressPoint(hostFcp)
+ .constraints(buildConstraints(L2NETWORK_CONSTRAINTS, l2Network.encapsulation()))
+ .priority(SimpleFabricService.PRI_L2NETWORK_UNICAST)
+ .resourceGroup(resourceGroup);
+ uniIntents.add(intentBuilder.build());
+ });
+
+ return uniIntents;
+ }
+
+ // Intent generate utilities
+
+ private Set<Host> hostsFromL2Network(L2Network l2Network) {
+ Set<Interface> interfaces = l2Network.interfaces();
+ return interfaces.stream()
+ .map(this::hostsFromInterface)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+ }
+
+ private Set<Host> hostsFromInterface(Interface iface) {
+ return hostService.getConnectedHosts(iface.connectPoint())
+ .stream()
+ .filter(host -> host.vlan().equals(iface.vlan()))
+ .collect(Collectors.toSet());
+ }
+
+ private Key buildKey(String l2NetworkName, String type, ConnectPoint cPoint, MacAddress dstMac) {
+ return Key.of(l2NetworkName + "-" + type + "-" + cPoint.toString() + "-" + dstMac, l2ForwardAppId);
+ }
+
+ private List<Constraint> buildConstraints(List<Constraint> constraints, EncapsulationType encapsulation) {
+ if (!encapsulation.equals(EncapsulationType.NONE)) {
+ List<Constraint> newConstraints = new ArrayList<>(constraints);
+ constraints.stream()
+ .filter(c -> c instanceof EncapsulationConstraint)
+ .forEach(newConstraints::remove);
+ newConstraints.add(new EncapsulationConstraint(encapsulation));
+ return ImmutableList.copyOf(newConstraints);
+ }
+ return constraints;
+ }
+
+ private FilteredConnectPoint buildFilteredConnectedPoint(Interface iface) {
+ Objects.requireNonNull(iface);
+ TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+ if (iface.vlan() != null && !iface.vlan().equals(VlanId.NONE)) {
+ trafficSelector.matchVlanId(iface.vlan());
+ }
+ return new FilteredConnectPoint(iface.connectPoint(), trafficSelector.build());
+ }
+
+ protected FilteredConnectPoint buildFilteredConnectedPoint(Host host) {
+ Objects.requireNonNull(host);
+ TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+ if (host.vlan() != null && !host.vlan().equals(VlanId.NONE)) {
+ trafficSelector.matchVlanId(host.vlan());
+ }
+ return new FilteredConnectPoint(host.location(), trafficSelector.build());
+ }
+
+ // Dump command handler
+ private void dump(String subject, PrintStream out) {
+ if ("intents".equals(subject)) {
+ out.println("L2Forward Broadcast Intents:\n");
+ for (SinglePointToMultiPointIntent intent: bctIntentsMap.values()) {
+ out.println(" " + intent.key().toString()
+ + ": " + intent.selector().criteria()
+ + ", [" + intent.filteredIngressPoint().connectPoint()
+ + "] -> " + intent.filteredEgressPoints().stream()
+ .map(FilteredConnectPoint::connectPoint).collect(Collectors.toSet()));
+ }
+ out.println("");
+ out.println("L2Forward Unicast Intents:\n");
+ for (MultiPointToSinglePointIntent intent: uniIntentsMap.values()) {
+ out.println(" " + intent.key().toString()
+ + ": " + intent.selector().criteria()
+ + ", [" + intent.filteredIngressPoints().stream()
+ .map(FilteredConnectPoint::connectPoint).collect(Collectors.toSet())
+ + "] -> " + intent.filteredEgressPoint().connectPoint());
+ }
+ out.println("");
+ out.println("L2Forward Intents to Be Purged:\n");
+ for (Key key: toBePurgedIntentKeys) {
+ out.println(" " + key.toString());
+ }
+ out.println("");
+ }
+ }
+
+ // Listener
+ private class InternalSimpleFabricListener implements SimpleFabricListener {
+ @Override
+ public void event(SimpleFabricEvent event) {
+ switch (event.type()) {
+ case SIMPLE_FABRIC_UPDATED:
+ refresh();
+ checkIntentsPurge();
+ break;
+ case SIMPLE_FABRIC_IDLE:
+ refresh();
+ checkIntentsPurge();
+ break;
+ case SIMPLE_FABRIC_DUMP:
+ dump(event.subject(), event.out());
+ break;
+ default:
+ // NOTE: nothing to do on SIMPLE_FABRIC_FLUSH
+ break;
+ }
+ }
+ }
+
+}