[ONOS-5283] Arbitrary connect points, support multiple vlans

Change-Id: I9bd3536c08dfd8a637293460395de7e2a1dc1dc1
This commit is contained in:
nosignal 2016-09-16 16:11:40 -07:00 committed by Luca Prete
parent 8d4e8bccdf
commit 5fd282e642
10 changed files with 1880 additions and 525 deletions

View File

@ -15,18 +15,18 @@
*/
package org.onosproject.vpls;
import com.google.common.collect.SetMultimap;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.FilteredConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
import org.onosproject.net.intent.SinglePointToMultiPointIntent;
@ -34,24 +34,37 @@ import org.onosproject.routing.IntentSynchronizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Synchronizes intents between the in-memory intent store and the
* IntentService.
*/
public class IntentInstaller {
private static final String SUBMIT =
"Submitting intents to the Intent Synchronizer";
private static final String WITHDRAW =
"Withdrawing intents to the Intent Synchronizer";
private static final String SP2MP =
"Building sp2mp intent from {}";
private static final String MP2SP =
"Building mp2sp intent to {}";
private static final Logger log = LoggerFactory.getLogger(
IntentInstaller.class);
private static final int PRIORITY_OFFSET = 1000;
private static final String PREFIX_BROADCAST = "brc";
private static final String PREFIX_UNICAST = "uni";
private static final Set<IntentState> WITHDRAWN_INTENT_STATES =
ImmutableSet.of(IntentState.WITHDRAWN,
IntentState.WITHDRAW_REQ,
IntentState.WITHDRAWING);
static final String PREFIX_BROADCAST = "brc";
static final String PREFIX_UNICAST = "uni";
static final String DASH = "-";
private final ApplicationId appId;
private final IntentSynchronizationService intentSynchronizer;
@ -71,174 +84,151 @@ public class IntentInstaller {
this.intentSynchronizer = intentSynchronizer;
}
/**
* Formats the requests for creating and submit intents.
* Single Points to Multi Point intents are created for all the configured
* Connect Points. Multi Point to Single Point intents are created for
* Connect Points configured that have hosts attached.
*
* @param confHostPresentCPoint A map of Connect Points with the eventual
* MAC address of the host attached, by VLAN
*/
protected void installIntents(SetMultimap<VlanId,
Pair<ConnectPoint,
MacAddress>> confHostPresentCPoint) {
List<Intent> intents = new ArrayList<>();
confHostPresentCPoint.keySet()
.stream()
.filter(vlanId -> confHostPresentCPoint.get(vlanId) != null)
.forEach(vlanId -> {
Set<Pair<ConnectPoint, MacAddress>> cPoints =
confHostPresentCPoint.get(vlanId);
cPoints.forEach(cPoint -> {
MacAddress mac = cPoint.getValue();
ConnectPoint src = cPoint.getKey();
Set<ConnectPoint> dsts = cPoints.stream()
.map(Pair::getKey)
.filter(cp -> !cp.equals(src))
.collect(Collectors.toSet());
Key brcKey = buildKey(PREFIX_BROADCAST, src, vlanId);
if (dsts.isEmpty()) {
return;
}
intents.add(buildBrcIntent(brcKey, src, dsts, vlanId));
if (mac != null && countMacInCPoints(cPoints) > 1) {
Key uniKey = buildKey(PREFIX_UNICAST, src, vlanId);
MultiPointToSinglePointIntent uniIntent =
buildUniIntent(uniKey,
dsts,
src,
vlanId,
mac);
intents.add(uniIntent);
}
});
});
submitIntents(intents);
}
/**
* Requests to install the intents passed as argument to the Intent Service.
*
* @param intents intents to be submitted
*/
private void submitIntents(Collection<Intent> intents) {
log.debug("Submitting intents to the Intent Synchronizer");
intents.forEach(intent -> {
intentSynchronizer.submit(intent);
});
protected void submitIntents(Collection<Intent> intents) {
log.debug(SUBMIT);
intents.forEach(intentSynchronizer::submit);
}
/**
* Builds a Single Point to Multi Point intent.
* Requests to withdraw the intents passed as argument to the Intent Service.
*
* @param src The source Connect Point
* @param dsts The destination Connect Points
* @return Single Point to Multi Point intent generated.
* @param intents intents to be withdraw
*/
private SinglePointToMultiPointIntent buildBrcIntent(Key key,
ConnectPoint src,
Set<ConnectPoint> dsts,
VlanId vlanId) {
log.debug("Building p2mp intent from {}", src);
protected void withdrawIntents(Collection<Intent> intents) {
log.debug(WITHDRAW);
intents.forEach(intentSynchronizer::withdraw);
}
/**
* Returns list of intents belongs to a VPLS.
*
* @param name required VPLS network name
* @return list of intents belongs to a VPLS
*/
protected List<Intent> getIntentsFromVpls(String name) {
List<Intent> intents = Lists.newArrayList();
intentService.getIntents().forEach(intent -> {
if (intent.key().toString().startsWith(name)) {
intents.add(intent);
}
});
return intents;
}
/**
* Builds a broadcast intent.
*
* @param key key to identify the intent
* @param src the source connect point
* @param dsts the destination connect points
* @return the generated single-point to multi-point intent
*/
protected SinglePointToMultiPointIntent buildBrcIntent(Key key,
FilteredConnectPoint src,
Set<FilteredConnectPoint> dsts) {
log.debug(SP2MP, src);
SinglePointToMultiPointIntent intent;
TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthDst(MacAddress.BROADCAST)
.matchVlanId(vlanId);
TrafficSelector selector = builder.build();
.build();
intent = SinglePointToMultiPointIntent.builder()
.appId(appId)
.key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(src)
.egressPoints(dsts)
.filteredIngressPoint(src)
.filteredEgressPoints(dsts)
.priority(PRIORITY_OFFSET)
.build();
return intent;
}
/**
* Builds a Multi Point to Single Point intent.
* Builds a unicast intent.
*
* @param srcs The source Connect Points
* @param dst The destination Connect Point
* @return Multi Point to Single Point intent generated.
* @param key key to identify the intent
* @param srcs the source Connect Points
* @param dst the destination Connect Point
* @param host destination Host
* @return the generated multi-point to single-point intent
*/
private MultiPointToSinglePointIntent buildUniIntent(Key key,
Set<ConnectPoint> srcs,
ConnectPoint dst,
VlanId vlanId,
MacAddress mac) {
log.debug("Building mp2p intent to {}", dst);
protected MultiPointToSinglePointIntent buildUniIntent(Key key,
Set<FilteredConnectPoint> srcs,
FilteredConnectPoint dst,
Host host) {
log.debug(MP2SP, dst);
MultiPointToSinglePointIntent intent;
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthDst(host.mac())
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
.matchEthDst(mac)
.matchVlanId(vlanId);
TrafficSelector selector = builder.build();
intent = MultiPointToSinglePointIntent.builder()
return MultiPointToSinglePointIntent.builder()
.appId(appId)
.key(key)
.selector(selector)
.treatment(treatment)
.ingressPoints(srcs)
.egressPoint(dst)
.filteredIngressPoints(srcs)
.filteredEgressPoint(dst)
.priority(PRIORITY_OFFSET)
.build();
return intent;
}
/**
* Builds an intent Key for either for a Single Point to Multi Point or
* Multi Point to Single Point intent, based on a prefix that defines
* Builds an intent Key for either for a single-point to multi-point or
* multi-point to single-point intent, based on a prefix that defines
* the type of intent, the single connection point representing the source
* or the destination and the vlan id representing the network.
* or the destination and the VLAN identifier representing the network.
*
* @param cPoint the source or destination connect point
* @param vlanId the network vlan id
* @param prefix prefix string
* @return
* @param prefix key prefix
* @param cPoint connect point for single source/destination
* @param networkName VPLS network name
* @param hostMac source/destination mac address
* @return key to identify the intent
*/
private Key buildKey(String prefix, ConnectPoint cPoint, VlanId vlanId) {
String keyString = new StringBuilder()
.append(prefix)
.append("-")
.append(cPoint.deviceId())
.append("-")
.append(cPoint.port())
.append("-")
.append(vlanId)
.toString();
protected Key buildKey(String prefix,
ConnectPoint cPoint,
String networkName,
MacAddress hostMac) {
String keyString = networkName +
DASH +
prefix +
DASH +
cPoint.deviceId() +
DASH +
cPoint.port() +
DASH +
hostMac;
return Key.of(keyString, appId);
}
/**
* Counts the number of mac addresses associated to a specific list of
* ConnectPoint.
* Returns true if the specified intent exists; false otherwise.
*
* @param cPoints Set of ConnectPoints, eventually bound to the MAC of the
* host attached
* @return number of mac addresses found.
* @param intentKey intent key
* @return true if the intent exists, false otherwise
*/
private int countMacInCPoints(Set<Pair<ConnectPoint, MacAddress>> cPoints) {
return (int) cPoints.stream().filter(p -> p.getValue() != null).count();
}
protected boolean intentExists(Key intentKey) {
if (intentService.getIntent(intentKey) == null) {
return false;
}
// Intent does not exist if intent withdrawn
IntentState currentIntentState = intentService.getIntentState(intentKey);
if (WITHDRAWN_INTENT_STATES.contains(currentIntentState)) {
return false;
}
return true;
}
}

View File

@ -15,10 +15,10 @@
*/
package org.onosproject.vpls;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.SetMultimap;
import org.apache.commons.lang3.tuple.Pair;
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;
@ -29,29 +29,56 @@ import org.onlab.packet.VlanId;
import org.onosproject.app.ApplicationService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceEvent;
import org.onosproject.incubator.net.intf.InterfaceListener;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.FilteredConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.Key;
import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.vpls.config.VplsConfigurationService;
import org.slf4j.Logger;
import java.util.Map;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
/**
* Application to create L2 broadcast overlay networks using VLAN.
*/
@Component(immediate = true)
public class Vpls {
protected static final String VPLS_APP = "org.onosproject.vpls";
/**
* Application name of VPLS.
*/
static final String VPLS_APP = "org.onosproject.vpls";
private static final String HOST_FCP_NOT_FOUND =
"Filtered connected point for host {} not found";
private static final String HOST_EVENT = "Received HostEvent {}";
private static final String INTF_CONF_EVENT =
"Received InterfaceConfigEvent {}";
private static final String NET_CONF_EVENT =
"Received NetworkConfigEvent {}";
private final Logger log = getLogger(getClass());
@ -73,11 +100,20 @@ public class Vpls {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentSynchronizationService intentSynchronizer;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VplsConfigurationService vplsConfigService;
private final HostListener hostListener = new InternalHostListener();
private final InternalInterfaceListener interfaceListener
= new InternalInterfaceListener();
private final InternalNetworkConfigListener configListener =
new InternalNetworkConfigListener();
private IntentInstaller intentInstaller;
private ApplicationId appId;
@ -97,111 +133,250 @@ public class Vpls {
hostService.addListener(hostListener);
interfaceService.addListener(interfaceListener);
configService.addListener(configListener);
setupConnectivity();
setupConnectivity(false);
log.info("Activated");
}
@Deactivate
public void deactivate() {
configService.removeListener(configListener);
intentSynchronizer.removeIntentsByAppId(appId);
log.info("Deactivated");
}
protected void setupConnectivity() {
/*
* Parse Configuration and get Connect Point by VlanId.
*/
SetMultimap<VlanId, ConnectPoint> confCPointsByVlan = getConfigCPoints();
/**
* Sets up connectivity for all VPLSs.
*
* @param isNetworkConfigEvent true if this function is triggered
* by NetworkConfigEvent; false otherwise
*/
private void setupConnectivity(boolean isNetworkConfigEvent) {
SetMultimap<String, Interface> networkInterfaces =
vplsConfigService.getVplsNetworks();
/*
* Check that configured Connect Points have hosts attached and
* associate their Mac Address to the Connect Points configured.
*/
SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> confHostPresentCPoint =
pairAvailableHosts(confCPointsByVlan);
Set<String> vplsAffectedByApi =
new HashSet<>(vplsConfigService.getVplsAffectedByApi());
/*
* Create and submit intents between the Connect Points.
* Intents for broadcast between all the configured Connect Points.
* Intents for unicast between all the configured Connect Points with
* hosts attached.
*/
intentInstaller.installIntents(confHostPresentCPoint);
if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
vplsAffectedByApi.addAll(vplsConfigService.getOldVpls());
}
networkInterfaces.asMap().forEach((networkName, interfaces) -> {
Set<Host> hosts = Sets.newHashSet();
interfaces.forEach(intf -> {
// Add hosts that belongs to the specific VPLS
hostService.getConnectedHosts(intf.connectPoint())
.stream()
.filter(host -> host.vlan().equals(intf.vlan()))
.forEach(hosts::add);
});
setupConnectivity(networkName, interfaces, hosts,
vplsAffectedByApi.contains(networkName));
vplsAffectedByApi.remove(networkName);
});
if (!vplsAffectedByApi.isEmpty()) {
for (String networkName:vplsAffectedByApi) {
withdrawIntents(networkName, Lists.newArrayList());
}
}
}
/**
* Computes the list of configured interfaces with a VLAN Id.
* Sets up connectivity for specific VPLS.
*
* @return the interfaces grouped by vlan id
* @param networkName the VPLS name
* @param interfaces the interfaces that belong to the VPLS
* @param hosts the hosts that belong to the VPLS
* @param affectedByApi true if this function is triggered from the APIs;
* false otherwise
*/
protected SetMultimap<VlanId, ConnectPoint> getConfigCPoints() {
log.debug("Checking interface configuration");
private void setupConnectivity(String networkName,
Collection<Interface> interfaces,
Set<Host> hosts,
boolean affectedByApi) {
List<Intent> intents = Lists.newArrayList();
List<Key> keys = Lists.newArrayList();
Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
SetMultimap<VlanId, ConnectPoint> confCPointsByVlan =
HashMultimap.create();
intents.addAll(buildUnicastIntents(
networkName, hosts, fcPoints, affectedByApi));
intents.addAll(buildBroadcastIntents(
networkName, fcPoints, affectedByApi));
interfaceService.getInterfaces()
.stream()
.filter(intf -> intf.ipAddressesList().isEmpty())
.forEach(intf -> confCPointsByVlan.put(intf.vlan(), intf.connectPoint()));
return confCPointsByVlan;
if (affectedByApi) {
intents.forEach(intent -> keys.add(intent.key()));
withdrawIntents(networkName, keys);
}
intentInstaller.submitIntents(intents);
}
/**
* Checks if for any ConnectPoint configured there's an host presents
* and in case it associates them together.
* Withdraws intents belonging to a VPLS, given a VPLS name.
*
* @param confCPointsByVlan the configured ConnectPoints grouped by VLAN Id
* @return the configured ConnectPoints with eventual hosts associated.
* @param networkName the VPLS name
* @param keys the keys of the intents to be installed
*/
protected SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> pairAvailableHosts(
SetMultimap<VlanId, ConnectPoint> confCPointsByVlan) {
log.debug("Binding connected hosts MAC addresses");
private void withdrawIntents(String networkName,
List<Key> keys) {
List<Intent> intents = Lists.newArrayList();
SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> confHostPresentCPoint =
HashMultimap.create();
intentInstaller.getIntentsFromVpls(networkName)
.forEach(intent -> {
if (!keys.contains(intent.key())) {
intents.add(intent);
}
});
confCPointsByVlan.entries()
.forEach(e -> bindMacAddr(e, confHostPresentCPoint));
return confHostPresentCPoint;
intentInstaller.withdrawIntents(intents);
}
// Bind VLAN Id with hosts and connect points
private void bindMacAddr(Map.Entry<VlanId, ConnectPoint> e,
SetMultimap<VlanId, Pair<ConnectPoint,
MacAddress>> confHostPresentCPoint) {
VlanId vlanId = e.getKey();
ConnectPoint cp = e.getValue();
Set<Host> connectedHosts = hostService.getConnectedHosts(cp);
connectedHosts.forEach(host -> {
if (host.vlan().equals(vlanId)) {
confHostPresentCPoint.put(vlanId, Pair.of(cp, host.mac()));
} else {
confHostPresentCPoint.put(vlanId, Pair.of(cp, null));
/**
* Sets up broadcast intents between any given filtered connect point.
*
* @param networkName the VPLS name
* @param fcPoints the set of filtered connect points
* @param affectedByApi true if the function triggered from APIs;
* false otherwise
* @return the set of broadcast intents
*/
private Set<Intent> buildBroadcastIntents(String networkName,
Set<FilteredConnectPoint> fcPoints,
boolean affectedByApi) {
Set<Intent> intents = Sets.newHashSet();
fcPoints.forEach(point -> {
Set<FilteredConnectPoint> otherPoints =
fcPoints.stream()
.filter(fcp -> !fcp.equals(point))
.collect(Collectors.toSet());
Key brcKey = intentInstaller.buildKey(PREFIX_BROADCAST,
point.connectPoint(),
networkName,
MacAddress.BROADCAST);
if ((!intentInstaller.intentExists(brcKey) || affectedByApi) &&
!otherPoints.isEmpty()) {
intents.add(intentInstaller.buildBrcIntent(brcKey,
point,
otherPoints));
}
});
if (connectedHosts.isEmpty()) {
confHostPresentCPoint.put(vlanId, Pair.of(cp, null));
}
return ImmutableSet.copyOf(intents);
}
/**
* Sets up unicast intents between any given filtered connect point.
*
* @param networkName the VPLS name
* @param hosts the set of destination hosts
* @param fcPoints the set of filtered connect points
* @param affectedByApi true if the function triggered from APIs;
* false otherwise
* @return the set of unicast intents
*/
private Set<Intent> buildUnicastIntents(String networkName,
Set<Host> hosts,
Set<FilteredConnectPoint> fcPoints,
boolean affectedByApi) {
Set<Intent> intents = Sets.newHashSet();
hosts.forEach(host -> {
FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
if (hostPoint == null) {
log.warn(HOST_FCP_NOT_FOUND, host);
return;
}
Set<FilteredConnectPoint> otherPoints =
fcPoints.stream()
.filter(fcp -> !fcp.equals(hostPoint))
.collect(Collectors.toSet());
Key uniKey = intentInstaller.buildKey(PREFIX_UNICAST,
host.location(),
networkName,
host.mac());
if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
!otherPoints.isEmpty()) {
intents.add(intentInstaller.buildUniIntent(uniKey,
otherPoints,
hostPoint,
host));
}
});
return ImmutableSet.copyOf(intents);
}
/**
* Finds the filtered connect point a host is attached to.
*
* @param host the target host
* @param fcps the filtered connected points
* @return null if not found; the filtered connect point otherwise
*/
private FilteredConnectPoint getHostPoint(Host host,
Set<FilteredConnectPoint> fcps) {
return fcps.stream()
.filter(fcp -> fcp.connectPoint().equals(host.location()))
.filter(fcp -> {
VlanIdCriterion vlanCriterion =
(VlanIdCriterion) fcp.trafficSelector().
getCriterion(Criterion.Type.VLAN_VID);
return vlanCriterion != null &&
vlanCriterion.vlanId().equals(host.vlan());
})
.findFirst()
.orElse(null);
}
/**
* Computes a set of filtered connect points from a list of given interfaces.
*
* @param interfaces the interfaces to compute
* @return the set of filtered connect points
*/
private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
// Build all filtered connected points in the network
return interfaces
.stream()
.map(intf -> {
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
if (!intf.vlan().equals(VlanId.NONE)) {
selectorBuilder.matchVlanId(intf.vlan());
}
return new FilteredConnectPoint(intf.connectPoint(),
selectorBuilder.build());
})
.collect(Collectors.toSet());
}
/**
* Listener for host events.
*/
class InternalHostListener implements HostListener {
private class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
log.debug("Received HostEvent {}", event);
log.debug(HOST_EVENT, event);
switch (event.type()) {
case HOST_ADDED:
case HOST_UPDATED:
case HOST_REMOVED:
setupConnectivity();
setupConnectivity(false);
break;
default:
break;
}
@ -214,16 +389,39 @@ public class Vpls {
private class InternalInterfaceListener implements InterfaceListener {
@Override
public void event(InterfaceEvent event) {
log.debug("Received InterfaceConfigEvent {}", event);
log.debug(INTF_CONF_EVENT, event);
switch (event.type()) {
case INTERFACE_ADDED:
case INTERFACE_UPDATED:
case INTERFACE_REMOVED:
setupConnectivity();
setupConnectivity(false);
break;
default:
break;
}
}
}
/**
* Listener for VPLS configuration events.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
log.debug(NET_CONF_EVENT, event.configClass());
switch (event.type()) {
case CONFIG_ADDED:
case CONFIG_UPDATED:
case CONFIG_REMOVED:
setupConnectivity(true);
break;
default:
break;
}
}
}
}
}

View File

@ -0,0 +1,194 @@
/*
* 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.vpls.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Sets;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.config.Config;
import java.util.Set;
/**
* Configuration object for VPLS config.
*/
public class VplsConfig extends Config<ApplicationId> {
private static final String VPLS = "vplsNetworks";
private static final String NAME = "name";
private static final String INTERFACE = "interfaces";
/**
* Returns a set of configured VPLSs.
*
* @return set of VPLSs
*/
public Set<VplsNetworkConfig> vplsNetworks() {
Set<VplsNetworkConfig> vpls = Sets.newHashSet();
JsonNode vplsNode = object.get(VPLS);
if (vplsNode == null) {
return vpls;
}
vplsNode.forEach(jsonNode -> {
Set<String> ifaces = Sets.newHashSet();
jsonNode.path(INTERFACE).forEach(ifacesNode ->
ifaces.add(ifacesNode.asText())
);
String name = jsonNode.get(NAME).asText();
vpls.add(new VplsNetworkConfig(name, ifaces));
});
return vpls;
}
/**
* Returns the VPLS configuration given a VPLS name.
*
* @param name the VPLS name
* @return the VPLS configuration if it exists; null otherwise
*/
public VplsNetworkConfig getVplsWithName(String name) {
for (VplsNetworkConfig vpls : vplsNetworks()) {
if (vpls.name().equals(name)) {
return vpls;
}
}
return null;
}
/**
* Adds a VPLS to the configuration.
*
* @param name the name of the VPLS to be added
*/
public void addVpls(VplsNetworkConfig name) {
ObjectNode vplsNode = JsonNodeFactory.instance.objectNode();
vplsNode.put(NAME, name.name());
ArrayNode ifacesNode = vplsNode.putArray(INTERFACE);
name.ifaces().forEach(ifacesNode::add);
ArrayNode vplsArray = vplsNetworks().isEmpty() ?
initVplsConfiguration() : (ArrayNode) object.get(VPLS);
vplsArray.add(vplsNode);
}
/**
* Removes a VPLS from the configuration.
*
* @param name the name of the VPLS to be removed
*/
public void removeVpls(String name) {
ArrayNode vplsArray = (ArrayNode) object.get(VPLS);
for (int i = 0; i < vplsArray.size(); i++) {
if (vplsArray.get(i).hasNonNull(NAME) &&
vplsArray.get(i).get(NAME).asText().equals(name)) {
vplsArray.remove(i);
return;
}
}
}
/**
* Finds a VPLS with a given network interface.
*
* @param iface the network interface
* @return the VPLS if found; null otherwise
*/
public VplsNetworkConfig getVplsFromInterface(String iface) {
for (VplsNetworkConfig vpls : vplsNetworks()) {
if (vpls.isAttached(iface)) {
return vpls;
}
}
return null;
}
/**
* Adds a network interface to a VPLS.
*
* @param name the name of the VPLS
* @param iface the network interface to be added
*/
public void addInterfaceToVpls(String name, String iface) {
JsonNode vplsNode = object.get(VPLS);
vplsNode.forEach(jsonNode -> {
if (hasNamedNode(jsonNode, name)) {
ArrayNode ifacesNode = (ArrayNode) jsonNode.get(INTERFACE);
for (int i = 0; i < ifacesNode.size(); i++) {
if (ifacesNode.get(i).asText().equals(iface)) {
return; // Interface already exists.
}
}
ifacesNode.add(iface);
}
});
}
/**
* Removes a network interface from a VPLS.
*
* @param name the name of the VPLS
* @param iface the network interface to be removed
*/
public void removeInterfaceFromVpls(VplsNetworkConfig name, String iface) {
JsonNode vplsNode = object.get(VPLS);
vplsNode.forEach(jsonNode -> {
if (hasNamedNode(jsonNode, name.name())) {
ArrayNode ifacesNode = (ArrayNode) jsonNode.get(INTERFACE);
for (int i = 0; i < ifacesNode.size(); i++) {
if (ifacesNode.get(i).asText().equals(iface)) {
ifacesNode.remove(i);
return;
}
}
}
});
}
/**
* States if a JSON node has a "name" attribute and if the value is equal to
* the name given.
*
* @param jsonNode the JSON node
* @param name the node name
* @return true if the JSON node has a "name" attribute with value equal to
* the name given; false otherwise
*/
private boolean hasNamedNode(JsonNode jsonNode, String name) {
return jsonNode.hasNonNull(NAME) &&
jsonNode.get(NAME).asText().equals(name);
}
/**
* Creates an empty VPLS configuration.
*
* @return empty ArrayNode to store the VPLS configuration
*/
private ArrayNode initVplsConfiguration() {
return object.putArray(VPLS);
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.vpls.config;
import com.google.common.collect.SetMultimap;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
import java.util.Set;
/**
* Provides information about the VPLS configuration.
*/
public interface VplsConfigurationService {
Class<VplsConfig> CONFIG_CLASS = VplsConfig.class;
/**
* Adds a VPLS to the configuration.
*
* @param name the name of the VPLS
* @param ifaces the interfaces associated with the VPLS
*/
void addVpls(String name, Set<String> ifaces);
/**
* Removes a VPLS from the configuration.
*
* @param name the name of the VPLS to be removed
*/
void removeVpls(String name);
/**
* Adds a network interface to a VPLS.
*
* @param name the name of the VPLS
* @param iface the network interface to be added to the VPLS
*/
void addInterfaceToVpls(String name, String iface);
/**
* Removes a network interface from a VPLS.
*
* @param iface the network interface to be removed from the VPLS
*/
void removeInterfaceFromVpls(String iface);
/**
* Cleans up the VPLS configuration. Removes all VPLSs.
*/
void cleanVpls();
/**
* Retrieves the VPLS names modified from CLI.
*
* @return a set of VPLS names modified from CLI
*/
Set<String> getVplsAffectedByApi();
// TODO Removes this function after intent framework fix race condition
/**
* Retrieves the interfaces from the VPLS configuration.
*
* @return a set of interfaces contained in the VPLS configuration
*/
Set<Interface> getAllInterfaces();
/**
* Retrieves the interfaces belonging to the VPLS.
*
* @param name the name of the VPLS
* @return a set of interfaces belonging to the VPLS
*/
Set<Interface> getVplsInterfaces(String name);
/**
* Retrieves all VPLS names.
*
* @return a set of VPLS names
*/
Set<String> getAllVpls();
/**
* Retrieves all VPLS names from the old config.
*
* @return a set of VPLS names
*/
Set<String> getOldVpls();
// TODO Removes this function after intent framework fix race condition
/**
* Retrieves the VPLS names and associated interfaces from the configuration.
*
* @return a map VPLS names and associated interfaces
*/
SetMultimap<String, Interface> getVplsNetworks();
/**
* Retrieves a VPLS network given a VLAN Id and a connect point.
*
* @param vlan the VLAN Id
* @param connectPoint the connect point
* @return a map VPLS names and associated interfaces; null otherwise
*/
SetMultimap<String, Interface> getVplsNetwork(VlanId vlan,
ConnectPoint connectPoint);
}

View File

@ -0,0 +1,88 @@
/*
* 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.vpls.config;
import com.google.common.collect.ImmutableSet;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Configuration of a VPLS Network.
*/
public class VplsNetworkConfig {
private final String name;
private final Set<String> ifaces;
/**
* Creates a new VPLS configuration.
*
* @param name the VPLS name
* @param ifaces the interfaces associated with the VPLS
*/
public VplsNetworkConfig(String name, Set<String> ifaces) {
this.name = checkNotNull(name);
this.ifaces = checkNotNull(ImmutableSet.copyOf(ifaces));
}
/**
* Returns the name of the VPLS.
*
* @return the name of the VPLS
*/
public String name() {
return name;
}
/**
* Returns the name of interfaces associated with the VPLS.
*
* @return a set of interface names associated with the VPLS
*/
public Set<String> ifaces() {
return ImmutableSet.copyOf(ifaces);
}
/**
* States if a given interface is part of a VPLS.
*
* @param iface the interface attached to a VPLS
* @return true if the interface is associated to the VPLS; false otherwise
*/
public boolean isAttached(String iface) {
return ifaces.stream().anyMatch(i -> i.equals(iface));
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof VplsNetworkConfig) {
final VplsNetworkConfig that = (VplsNetworkConfig) obj;
return Objects.equals(this.name, that.name) &&
Objects.equals(this.ifaces, that.ifaces);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(name, ifaces);
}
}

View File

@ -0,0 +1,335 @@
/*
* 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.vpls.config.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
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.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.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.vpls.config.VplsConfig;
import org.onosproject.vpls.config.VplsNetworkConfig;
import org.onosproject.vpls.config.VplsConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
/**
* Implementation of VPLSConfigurationService which reads VPLS configuration
* from the network configuration service.
*/
@Component(immediate = true)
@Service
public class VplsConfigurationImpl implements VplsConfigurationService {
private static final String VPLS_APP = "org.onosproject.vpls";
private static final String VPLS = "vpls";
private static final String EMPTY = "";
private static final String CONFIG_NULL = "VPLS configuration not defined";
private static final String APP_ID_NULL = "VPLS application ID is null";
private static final String CONFIG_CHANGED = "VPLS configuration changed: {}";
private static final String CHECK_CONFIG =
"Checking the interface configuration";
private static final String NET_CONF_EVENT =
"Received NetworkConfigEvent {}";
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry registry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected InterfaceService interfaceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService configService;
private final Set<String> vplsAffectedByApi = new HashSet<>();
private VplsConfig vplsConfig = new VplsConfig();
private SetMultimap<String, String> ifacesOfVpls = HashMultimap.create();
private SetMultimap<String, String> oldIfacesOfVpls = HashMultimap.create();
private SetMultimap<String, Interface> vplsNetworks = HashMultimap.create();
private final InternalNetworkConfigListener configListener =
new InternalNetworkConfigListener();
private ConfigFactory<ApplicationId, VplsConfig> vplsConfigFactory =
new ConfigFactory<ApplicationId, VplsConfig>(
SubjectFactories.APP_SUBJECT_FACTORY, VplsConfig.class, VPLS) {
@Override
public VplsConfig createConfig() {
return new VplsConfig();
}
};
private ApplicationId vplsAppId;
@Activate
protected void active() {
configService.addListener(configListener);
registry.registerConfigFactory(vplsConfigFactory);
loadConfiguration();
log.info("Started");
}
@Deactivate
protected void deactive() {
registry.unregisterConfigFactory(vplsConfigFactory);
configService.removeListener(configListener);
log.info("Stopped");
}
/**
* Retrieves the VPLS configuration from network configuration.
*/
private void loadConfiguration() {
loadAppId();
vplsConfig = configService.getConfig(vplsAppId, VplsConfig.class);
if (vplsConfig == null) {
log.warn(CONFIG_NULL);
configService.addConfig(vplsAppId, VplsConfig.class);
return;
}
oldIfacesOfVpls = ifacesOfVpls;
ifacesOfVpls = getConfigInterfaces();
vplsNetworks = getConfigCPoints();
log.debug(CONFIG_CHANGED, ifacesOfVpls);
}
/**
* Retrieves the application identifier from core service.
*/
private void loadAppId() {
vplsAppId = coreService.getAppId(VPLS_APP);
if (vplsAppId == null) {
log.warn(APP_ID_NULL);
}
}
/**
* Applies a given configuration to the VPLS application.
*/
private void applyConfig(VplsConfig vplsConfig) {
loadAppId();
configService.applyConfig(vplsAppId, VplsConfig.class, vplsConfig.node());
}
/**
* Retrieves the VPLS names and associated interfaces names from the configuration.
*
* @return a map VPLS names and associated interface names
*/
private SetMultimap<String, String> getConfigInterfaces() {
SetMultimap<String, String> confIntfByVpls =
HashMultimap.create();
vplsConfig.vplsNetworks().forEach(vpls -> {
if (vpls.ifaces().isEmpty()) {
confIntfByVpls.put(vpls.name(), EMPTY);
} else {
vpls.ifaces().forEach(iface -> confIntfByVpls.put(vpls.name(), iface));
}
});
return confIntfByVpls;
}
/**
* Retrieves the VPLS names and associated interfaces from the configuration.
*
* @return a map VPLS names and associated interfaces
*/
private SetMultimap<String, Interface> getConfigCPoints() {
log.debug(CHECK_CONFIG);
SetMultimap<String, Interface> confCPointsByIntf =
HashMultimap.create();
ifacesOfVpls.entries().forEach(vpls -> {
interfaceService.getInterfaces()
.stream()
.filter(intf -> intf.ipAddressesList().isEmpty())
.filter(intf -> intf.name().equals(vpls.getValue()))
.forEach(intf -> confCPointsByIntf.put(vpls.getKey(), intf));
});
return confCPointsByIntf;
}
/**
* Listener for VPLS configuration events.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
log.debug(NET_CONF_EVENT, event.configClass());
switch (event.type()) {
case CONFIG_ADDED:
case CONFIG_UPDATED:
case CONFIG_REMOVED:
loadConfiguration();
break;
default:
break;
}
}
}
}
@Override
public void addVpls(String name, Set<String> ifaces) {
VplsNetworkConfig vpls;
if (ifacesOfVpls.containsKey(name)) {
if (ifaces.isEmpty()) {
return;
}
ifaces.forEach(iface ->
vplsConfig.addInterfaceToVpls(name, iface));
} else {
vpls = new VplsNetworkConfig(name, ifaces);
vplsConfig.addVpls(vpls);
}
vplsAffectedByApi.add(name);
applyConfig(vplsConfig);
}
@Override
public void removeVpls(String name) {
if (ifacesOfVpls.containsKey(name)) {
vplsConfig.removeVpls(name);
vplsAffectedByApi.add(name);
applyConfig(vplsConfig);
}
}
@Override
public void addInterfaceToVpls(String name, String iface) {
if (ifacesOfVpls.containsKey(name)) {
vplsConfig.addInterfaceToVpls(name, iface);
vplsAffectedByApi.add(name);
applyConfig(vplsConfig);
}
}
@Override
public void removeInterfaceFromVpls(String iface) {
if (ifacesOfVpls.containsValue(iface)) {
VplsNetworkConfig vpls = vplsConfig.getVplsFromInterface(iface);
vplsConfig.removeInterfaceFromVpls(vpls, iface);
vplsAffectedByApi.add(vpls.name());
applyConfig(vplsConfig);
}
}
@Override
public void cleanVpls() {
ifacesOfVpls.entries().forEach(e -> {
vplsConfig.removeVpls(e.getKey());
vplsAffectedByApi.add(e.getKey());
});
applyConfig(vplsConfig);
}
@Override
public Set<String> getVplsAffectedByApi() {
Set<String> vplsNames = ImmutableSet.copyOf(vplsAffectedByApi);
vplsAffectedByApi.clear();
return vplsNames;
}
@Override
public Set<Interface> getAllInterfaces() {
Set<Interface> allInterfaces = new HashSet<>();
vplsNetworks.values().forEach(allInterfaces::add);
return allInterfaces;
}
@Override
public Set<Interface> getVplsInterfaces(String name) {
Set<Interface> vplsInterfaces = new HashSet<>();
vplsNetworks.get(name).forEach(vplsInterfaces::add);
return vplsInterfaces;
}
@Override
public Set<String> getAllVpls() {
return ifacesOfVpls.keySet();
}
@Override
public Set<String> getOldVpls() {
return oldIfacesOfVpls.keySet();
}
@Override
public SetMultimap<String, Interface> getVplsNetworks() {
return ImmutableSetMultimap.copyOf(vplsNetworks);
}
@Override
public SetMultimap<String, Interface> getVplsNetwork(VlanId vlan,
ConnectPoint connectPoint) {
String vplsNetworkName =
vplsNetworks.entries().stream()
.filter(e -> e.getValue().connectPoint().equals(connectPoint))
.filter(e -> e.getValue().vlan().equals(vlan))
.map(e -> e.getKey())
.findFirst()
.orElse(null);
SetMultimap<String, Interface> result = HashMultimap.create();
if (vplsNetworkName != null && vplsNetworks.containsKey(vplsNetworkName)) {
vplsNetworks.get(vplsNetworkName)
.forEach(intf -> result.put(vplsNetworkName, intf));
return result;
}
return null;
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Configuration implementation to create L2 broadcast network using VLAN.
*/
package org.onosproject.vpls.config.impl;

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Configuration to create L2 broadcast network using VLAN.
*/
package org.onosproject.vpls.config;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
/*
* 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.vpls.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.ConfigApplyDelegate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests for the {@link VplsConfig} class.
*/
public class VplsConfigTest {
private static final String APP_NAME = "org.onosproject.vpls";
private static final ApplicationId APP_ID = new TestApplicationId(APP_NAME);
private static final String VPLS = "vplsNetworks";
private static final String NAME = "name";
private static final String INTERFACE = "interfaces";
private static final String NET1 = "net1";
private static final String NET2 = "net2";
private static final String NEWNET = "newnet";
private static final String IF1 = "sw5-4-100";
private static final String IF2 = "sw5-4-200";
private static final String IF3 = "sw5-5-100";
private static final String IF4 = "sw6-5-100";
private static final String IF5 = "sw6-5-400";
private static final String IF_NON_EXIST = "sw7-5-100";
private static final String JSON_TREE = "{\"" + VPLS +
"\" : [{\"" + NAME + "\" : \"net1\"," +
"\"" + INTERFACE + "\" : [" +
"\"sw5-4-100\",\"sw5-4-200\",\"sw5-5-100\"]}]}";
private static final String EMPTY_JSON_TREE = "{}";
private final ObjectMapper mapper = new ObjectMapper();
private final ConfigApplyDelegate delegate = new MockCfgDelegate();
private final VplsNetworkConfig initialNetwork = createInitialNetwork();
private Set<VplsNetworkConfig> networks = new HashSet<>();
private VplsConfig vplsConfig = new VplsConfig();
private VplsConfig emptyVplsConfig = new VplsConfig();
@Before
public void setUp() throws Exception {
JsonNode tree = new ObjectMapper().readTree(JSON_TREE);
vplsConfig.init(APP_ID, APP_NAME, tree, mapper, delegate);
JsonNode emptyTree = new ObjectMapper().readTree(EMPTY_JSON_TREE);
emptyVplsConfig.init(APP_ID, APP_NAME, emptyTree, mapper, delegate);
networks.add(initialNetwork);
}
/**
* Tests if a VPLS configuration can be retrieved from JSON.
*/
@Test
public void testVplsNetworks() {
assertEquals(networks, vplsConfig.vplsNetworks());
}
/**
* Tests an empty VPLS application configuration is retrieved from JSON.
*/
@Test
public void testEmptyVplsNetworks() {
assertTrue(emptyVplsConfig.vplsNetworks().isEmpty());
}
/**
* Tests if a VPLS can be found by name.
*/
@Test
public void testGetVplsWithName() {
assertNotNull(vplsConfig.getVplsWithName(NET1));
assertNull(vplsConfig.getVplsWithName(NET2));
}
/**
* Tests the addition of a new VPLS.
*/
@Test
public void testAddNetwork() {
int initialSize = vplsConfig.vplsNetworks().size();
VplsNetworkConfig newNetwork = createNewNetwork();
vplsConfig.addVpls(newNetwork);
assertEquals(initialSize + 1, vplsConfig.vplsNetworks().size());
networks.add(newNetwork);
assertEquals(networks, vplsConfig.vplsNetworks());
}
/**
* Tests the addition of new VPLS to an empty configuration.
*/
@Test
public void testAddNetworkToEmpty() {
VplsNetworkConfig newNetwork = createNewNetwork();
emptyVplsConfig.addVpls(newNetwork);
assertFalse(emptyVplsConfig.vplsNetworks().isEmpty());
}
/**
* Tests the removal of an existing VPLS from the configuration.
*/
@Test
public void testRemoveExistingNetwork() {
int initialSize = vplsConfig.vplsNetworks().size();
vplsConfig.removeVpls(NET1);
assertEquals(initialSize - 1, vplsConfig.vplsNetworks().size());
}
/**
* Tests the removal of a non-existing VPLS from the configuration.
*/
@Test
public void testRemoveInexistingNetwork() {
int initialSize = vplsConfig.vplsNetworks().size();
vplsConfig.removeVpls(NET2);
assertEquals(initialSize, vplsConfig.vplsNetworks().size());
}
/**
* Tests the addition of a new interface.
*/
@Test
public void testAddInterfaceToNetwork() {
int initialSize = vplsConfig.getVplsWithName(NET1).ifaces().size();
vplsConfig.addInterfaceToVpls(NET1, IF4);
assertEquals(initialSize + 1, vplsConfig.getVplsWithName(NET1).ifaces().size());
}
/**
* Tests the addition of a new interface when it already exists.
*/
@Test
public void testAddExistingInterfaceToNetwork() {
int initialSize = vplsConfig.getVplsWithName(NET1).ifaces().size();
vplsConfig.addInterfaceToVpls(NET1, IF1);
assertEquals(initialSize, vplsConfig.getVplsWithName(NET1).ifaces().size());
}
/**
* Tests the retrieval of a VPLS, given an interface name.
*/
@Test
public void testgetNetworkFromInterface() {
assertNotNull(vplsConfig.getVplsFromInterface(IF1));
assertNull(vplsConfig.getVplsFromInterface(IF_NON_EXIST));
}
/**
* Tests the removal of an interface.
*/
@Test
public void testRemoveExistingInterfaceFromNetwork() {
int initialSize = vplsConfig.getVplsWithName(NET1).ifaces().size();
vplsConfig.removeInterfaceFromVpls(initialNetwork, IF1);
assertEquals(initialSize - 1, vplsConfig.getVplsWithName(NET1).ifaces().size());
}
/**
* Tests the removal of an interface from a VPLS when it does not exist.
*/
@Test
public void testRemoveNonExistingInterfaceFromNetwork() {
int initialSize = vplsConfig.getVplsWithName(NET1).ifaces().size();
vplsConfig.removeInterfaceFromVpls(initialNetwork, IF_NON_EXIST);
assertEquals(initialSize, vplsConfig.getVplsWithName(NET1).ifaces().size());
}
/**
* Tests if the two interfaces are attached to the network
* while one of the interface is attached and another one is not.
*/
@Test
public void testIsAttached() {
VplsNetworkConfig network = createNewNetwork();
assertTrue(network.isAttached(IF4));
assertFalse(network.isAttached(IF_NON_EXIST));
}
private class MockCfgDelegate implements ConfigApplyDelegate {
@Override
public void onApply(@SuppressWarnings("rawtypes") Config config) {
config.apply();
}
}
private VplsNetworkConfig createInitialNetwork() {
Set<String> ifaces = new HashSet<>(Arrays.asList(IF1, IF2, IF3));
return new VplsNetworkConfig(NET1, ifaces);
}
private VplsNetworkConfig createNewNetwork() {
Set<String> ifaces = new HashSet<>(Arrays.asList(IF4, IF5));
return new VplsNetworkConfig(NEWNET, ifaces);
}
}