org.onosproject.simplefabric.SimpleFabricL2Forward.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.simplefabric.SimpleFabricL2Forward.java

Source

/*
 * 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.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;
            }
        }
    }

}