mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-11-03 01:31:21 +01:00
ONOS-985: Sample integration test application for group subsystem
Change-Id: I68352f922e5c7a0800fcc4fa839955769bf925a6
This commit is contained in:
parent
77bdd26ac6
commit
ed12ae5e81
72
apps/grouphandler/pom.xml
Normal file
72
apps/grouphandler/pom.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2014 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onos-apps</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>onos-app-grouphandler</artifactId>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<description>ONOS sample application using group service</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onos-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onlab-osgi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onlab-nio</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onlab-netty</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.karaf.shell</groupId>
|
||||
<artifactId>org.apache.karaf.shell.console</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.onosproject</groupId>
|
||||
<artifactId>onlab-misc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.Link;
|
||||
import org.onosproject.net.flow.DefaultTrafficTreatment;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
import org.onosproject.net.group.DefaultGroupBucket;
|
||||
import org.onosproject.net.group.GroupBucket;
|
||||
import org.onosproject.net.group.GroupBuckets;
|
||||
import org.onosproject.net.group.GroupService;
|
||||
import org.onosproject.net.link.LinkService;
|
||||
|
||||
/**
|
||||
* Default ECMP group handler creation module for an edge device.
|
||||
* This component creates a set of ECMP groups for every neighbor
|
||||
* that this device is connected to.
|
||||
* For example, consider a network of 4 devices: D0 (Segment ID: 100),
|
||||
* D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
|
||||
* where D0 and D3 are edge devices and D1 and D2 are transit devices.
|
||||
* Assume device D0 is connected to 2 neighbors (D1 and D2 ).
|
||||
* The following groups will be created in D0:
|
||||
* 1) all ports to D1 + with no label push,
|
||||
* 2) all ports to D1 + with label 102 pushed,
|
||||
* 3) all ports to D1 + with label 103 pushed,
|
||||
* 4) all ports to D2 + with no label push,
|
||||
* 5) all ports to D2 + with label 101 pushed,
|
||||
* 6) all ports to D2 + with label 103 pushed,
|
||||
* 7) all ports to D1 and D2 + with label 103 pushed
|
||||
*/
|
||||
public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
|
||||
|
||||
protected DefaultEdgeGroupHandler(DeviceId deviceId,
|
||||
ApplicationId appId,
|
||||
DeviceProperties config,
|
||||
LinkService linkService,
|
||||
GroupService groupService) {
|
||||
super(deviceId, appId, config, linkService, groupService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGroups() {
|
||||
log.debug("Creating default groups "
|
||||
+ "for edge device {}", deviceId);
|
||||
Set<DeviceId> neighbors = devicePortMap.keySet();
|
||||
if (neighbors == null || neighbors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create all possible Neighbor sets from this router
|
||||
Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(neighbors);
|
||||
log.trace("createGroupsAtEdgeRouter: The size of neighbor powerset "
|
||||
+ "for sw {} is {}", deviceId, powerSet.size());
|
||||
Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
|
||||
for (Set<DeviceId> combo : powerSet) {
|
||||
if (combo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Integer> groupSegmentIds =
|
||||
getSegmentIdsTobePairedWithNeighborSet(combo);
|
||||
for (Integer sId : groupSegmentIds) {
|
||||
NeighborSet ns = new NeighborSet(combo, sId);
|
||||
log.trace("createGroupsAtEdgeRouter: sw {} "
|
||||
+ "combo {} sId {} ns {}",
|
||||
deviceId, combo, sId, ns);
|
||||
nsSet.add(ns);
|
||||
}
|
||||
}
|
||||
log.trace("createGroupsAtEdgeRouter: The neighborset "
|
||||
+ "with label for sw {} is {}",
|
||||
deviceId, nsSet);
|
||||
|
||||
createGroupsFromNeighborsets(nsSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void newNeighbor(Link newNeighborLink) {
|
||||
log.debug("New Neighbor: Updating groups "
|
||||
+ "for edge device {}", deviceId);
|
||||
// Recompute neighbor power set
|
||||
addNeighborAtPort(newNeighborLink.dst().deviceId(),
|
||||
newNeighborLink.src().port());
|
||||
// Compute new neighbor sets due to the addition of new neighbor
|
||||
Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
|
||||
newNeighborLink.dst().deviceId(),
|
||||
devicePortMap.keySet());
|
||||
createGroupsFromNeighborsets(nsSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void newPortToExistingNeighbor(Link newNeighborLink) {
|
||||
log.debug("New port to existing neighbor: Updating "
|
||||
+ "groups for edge device {}", deviceId);
|
||||
addNeighborAtPort(newNeighborLink.dst().deviceId(),
|
||||
newNeighborLink.src().port());
|
||||
Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
|
||||
newNeighborLink.dst().deviceId(),
|
||||
devicePortMap.keySet());
|
||||
for (NeighborSet ns : nsSet) {
|
||||
// Create the new bucket to be updated
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(newNeighborLink.src().port())
|
||||
.setEthDst(deviceConfig.getDeviceMac(
|
||||
newNeighborLink.dst().deviceId()))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(ns.getEdgeLabel());
|
||||
GroupBucket updatedBucket = DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build());
|
||||
GroupBuckets updatedBuckets = new GroupBuckets(
|
||||
Arrays.asList(updatedBucket));
|
||||
log.debug("newPortToExistingNeighborAtEdgeRouter: "
|
||||
+ "groupService.addBucketsToGroup for neighborset{}", ns);
|
||||
groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
|
||||
DeviceId impactedNeighbor,
|
||||
Set<DeviceId> updatedNeighbors) {
|
||||
Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
|
||||
|
||||
Set<DeviceId> tmp = new HashSet<DeviceId>();
|
||||
tmp.addAll(updatedNeighbors);
|
||||
tmp.remove(impactedNeighbor);
|
||||
Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
|
||||
|
||||
// Compute the impacted neighbor sets
|
||||
powerSet.removeAll(tmpPowerSet);
|
||||
|
||||
Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
|
||||
for (Set<DeviceId> combo : powerSet) {
|
||||
if (combo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Integer> groupSegmentIds =
|
||||
getSegmentIdsTobePairedWithNeighborSet(combo);
|
||||
for (Integer sId : groupSegmentIds) {
|
||||
NeighborSet ns = new NeighborSet(combo, sId);
|
||||
log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
|
||||
+ "combo {} sId {} ns {}",
|
||||
deviceId, combo, sId, ns);
|
||||
nsSet.add(ns);
|
||||
}
|
||||
}
|
||||
log.trace("computeImpactedNeighborsetForPortEvent: The neighborset "
|
||||
+ "with label for sw {} is {}",
|
||||
deviceId, nsSet);
|
||||
return nsSet;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.onlab.packet.MacAddress;
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.Link;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.flow.DefaultTrafficTreatment;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
import org.onosproject.net.group.DefaultGroupBucket;
|
||||
import org.onosproject.net.group.DefaultGroupDescription;
|
||||
import org.onosproject.net.group.Group;
|
||||
import org.onosproject.net.group.GroupBucket;
|
||||
import org.onosproject.net.group.GroupBuckets;
|
||||
import org.onosproject.net.group.GroupDescription;
|
||||
import org.onosproject.net.group.GroupEvent;
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
import org.onosproject.net.group.GroupListener;
|
||||
import org.onosproject.net.group.GroupService;
|
||||
import org.onosproject.net.link.LinkService;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Default ECMP group handler creation module. This
|
||||
* component creates a set of ECMP groups for every neighbor
|
||||
* that this device is connected to based on whether the
|
||||
* current device is an edge device or a transit device.
|
||||
*/
|
||||
public class DefaultGroupHandler {
|
||||
protected final Logger log = getLogger(getClass());
|
||||
|
||||
protected final DeviceId deviceId;
|
||||
protected final ApplicationId appId;
|
||||
protected final DeviceProperties deviceConfig;
|
||||
protected final List<Integer> allSegmentIds;
|
||||
protected final int nodeSegmentId;
|
||||
protected final boolean isEdgeRouter;
|
||||
protected final MacAddress nodeMacAddr;
|
||||
protected LinkService linkService;
|
||||
protected GroupService groupService;
|
||||
|
||||
protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
|
||||
new HashMap<DeviceId, Set<PortNumber>>();
|
||||
protected HashMap<PortNumber, DeviceId> portDeviceMap =
|
||||
new HashMap<PortNumber, DeviceId>();
|
||||
|
||||
private GroupListener listener = new InternalGroupListener();
|
||||
|
||||
protected DefaultGroupHandler(DeviceId deviceId,
|
||||
ApplicationId appId,
|
||||
DeviceProperties config,
|
||||
LinkService linkService,
|
||||
GroupService groupService) {
|
||||
this.deviceId = checkNotNull(deviceId);
|
||||
this.appId = checkNotNull(appId);
|
||||
this.deviceConfig = checkNotNull(config);
|
||||
this.linkService = checkNotNull(linkService);
|
||||
this.groupService = checkNotNull(groupService);
|
||||
allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
|
||||
nodeSegmentId = config.getSegmentId(deviceId);
|
||||
isEdgeRouter = config.isEdgeDevice(deviceId);
|
||||
nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
|
||||
|
||||
this.groupService.addListener(listener);
|
||||
|
||||
populateNeighborMaps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group handler object based on the type of device. If
|
||||
* device is of edge type it returns edge group handler, else it
|
||||
* returns transit group handler.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @param appId application identifier
|
||||
* @param config interface to retrieve the device properties
|
||||
* @param linkService link service object
|
||||
* @param groupService group service object
|
||||
* @return default group handler type
|
||||
*/
|
||||
public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
|
||||
ApplicationId appId,
|
||||
DeviceProperties config,
|
||||
LinkService linkService,
|
||||
GroupService groupService) {
|
||||
if (config.isEdgeDevice(deviceId)) {
|
||||
return new DefaultEdgeGroupHandler(deviceId,
|
||||
appId,
|
||||
config,
|
||||
linkService,
|
||||
groupService);
|
||||
} else {
|
||||
return new DefaultTransitGroupHandler(deviceId,
|
||||
appId,
|
||||
config,
|
||||
linkService,
|
||||
groupService);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the auto created groups for this device based on the
|
||||
* current snapshot of the topology.
|
||||
*/
|
||||
//Empty implementations to be overridden by derived classes
|
||||
public void createGroups() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs group creation or update procedures when a new link
|
||||
* is discovered on this device.
|
||||
*
|
||||
* @param newLink new neighbor link
|
||||
*/
|
||||
public void linkUp(Link newLink) {
|
||||
if (newLink.type() != Link.Type.DIRECT) {
|
||||
log.warn("linkUp: unknown link type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newLink.src().deviceId().equals(deviceId)) {
|
||||
log.warn("linkUp: deviceId{} doesn't match with link src{}",
|
||||
deviceId,
|
||||
newLink.src().deviceId());
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Device {} linkUp at local port {} to neighbor {}",
|
||||
deviceId, newLink.src().port(), newLink.dst().deviceId());
|
||||
if (devicePortMap.get(newLink.dst().deviceId()) == null) {
|
||||
// New Neighbor
|
||||
newNeighbor(newLink);
|
||||
} else {
|
||||
// Old Neighbor
|
||||
newPortToExistingNeighbor(newLink);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs group recovery procedures when a port goes down
|
||||
* on this device.
|
||||
*
|
||||
* @param port port number that has gone down
|
||||
*/
|
||||
public void portDown(PortNumber port) {
|
||||
if (portDeviceMap.get(port) == null) {
|
||||
log.warn("portDown: unknown port");
|
||||
return;
|
||||
}
|
||||
log.debug("Device {} portDown {} to neighbor {}",
|
||||
deviceId, port, portDeviceMap.get(port));
|
||||
Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
|
||||
portDeviceMap.get(port),
|
||||
devicePortMap.keySet());
|
||||
for (NeighborSet ns : nsSet) {
|
||||
// Create the bucket to be removed
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(port)
|
||||
.setEthDst(deviceConfig.getDeviceMac(
|
||||
portDeviceMap.get(port)))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(ns.getEdgeLabel());
|
||||
GroupBucket removeBucket = DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build());
|
||||
GroupBuckets removeBuckets = new GroupBuckets(
|
||||
Arrays.asList(removeBucket));
|
||||
log.debug("portDown in device{}: "
|
||||
+ "groupService.removeBucketsFromGroup "
|
||||
+ "for neighborset{}", deviceId, ns);
|
||||
groupService.removeBucketsFromGroup(deviceId,
|
||||
ns,
|
||||
removeBuckets,
|
||||
ns,
|
||||
appId);
|
||||
}
|
||||
|
||||
devicePortMap.get(portDeviceMap.get(port)).remove(port);
|
||||
portDeviceMap.remove(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group associated with the key.
|
||||
*
|
||||
* @param key cookie associated with the group
|
||||
* @return group if found or null
|
||||
*/
|
||||
public Group getGroup(GroupKey key) {
|
||||
return groupService.getGroup(deviceId, key);
|
||||
}
|
||||
|
||||
//Empty implementation
|
||||
protected void newNeighbor(Link newLink) {
|
||||
}
|
||||
|
||||
//Empty implementation
|
||||
protected void newPortToExistingNeighbor(Link newLink) {
|
||||
}
|
||||
|
||||
//Empty implementation
|
||||
protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
|
||||
DeviceId impactedNeighbor,
|
||||
Set<DeviceId> updatedNeighbors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void populateNeighborMaps() {
|
||||
Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
|
||||
for (Link link:outgoingLinks) {
|
||||
if (link.type() != Link.Type.DIRECT) {
|
||||
continue;
|
||||
}
|
||||
addNeighborAtPort(link.dst().deviceId(), link.src().port());
|
||||
}
|
||||
}
|
||||
|
||||
protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
|
||||
// Update DeviceToPort database
|
||||
log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
|
||||
deviceId, neighborId, portToNeighbor);
|
||||
if (devicePortMap.get(neighborId) != null) {
|
||||
devicePortMap.get(neighborId).add(portToNeighbor);
|
||||
} else {
|
||||
Set<PortNumber> ports = new HashSet<PortNumber>();
|
||||
ports.add(portToNeighbor);
|
||||
devicePortMap.put(neighborId, ports);
|
||||
}
|
||||
|
||||
// Update portToDevice database
|
||||
if (portDeviceMap.get(portToNeighbor) == null) {
|
||||
portDeviceMap.put(portToNeighbor, neighborId);
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Set<DeviceId>>
|
||||
getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
|
||||
List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
|
||||
Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
|
||||
// get the number of elements in the neighbors
|
||||
int elements = list.size();
|
||||
// the number of members of a power set is 2^n
|
||||
// including the empty set
|
||||
int powerElements = (1 << elements);
|
||||
|
||||
// run a binary counter for the number of power elements
|
||||
// NOTE: Exclude empty set
|
||||
for (long i = 1; i < powerElements; i++) {
|
||||
Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
|
||||
for (int j = 0; j < elements; j++) {
|
||||
if ((i >> j) % 2 == 1) {
|
||||
neighborSubSet.add(list.get(j));
|
||||
}
|
||||
}
|
||||
sets.add(neighborSubSet);
|
||||
}
|
||||
return sets;
|
||||
}
|
||||
|
||||
private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
|
||||
return (deviceConfig.getSegmentId(deviceId) == sId);
|
||||
}
|
||||
|
||||
protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
|
||||
Set<DeviceId> neighbors) {
|
||||
|
||||
List<Integer> nsSegmentIds = new ArrayList<Integer>();
|
||||
|
||||
// Add one entry for "no label" (-1) to the list if
|
||||
// dpid list has not more than one node/neighbor as
|
||||
// there will never be a case a packet going to more than one
|
||||
// neighbor without a label at an edge router
|
||||
if (neighbors.size() == 1) {
|
||||
nsSegmentIds.add(-1);
|
||||
}
|
||||
// Filter out SegmentIds matching with the
|
||||
// nodes in the combo
|
||||
for (Integer sId : allSegmentIds) {
|
||||
if (sId.equals(nodeSegmentId)) {
|
||||
continue;
|
||||
}
|
||||
boolean filterOut = false;
|
||||
// Check if the edge label being set is of
|
||||
// any node in the Neighbor set
|
||||
for (DeviceId deviceId : neighbors) {
|
||||
if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
|
||||
filterOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!filterOut) {
|
||||
nsSegmentIds.add(sId);
|
||||
}
|
||||
}
|
||||
return nsSegmentIds;
|
||||
}
|
||||
|
||||
protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
|
||||
for (NeighborSet ns : nsSet) {
|
||||
// Create the bucket array from the neighbor set
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
for (DeviceId d : ns.getDeviceIds()) {
|
||||
for (PortNumber sp : devicePortMap.get(d)) {
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(sp)
|
||||
.setEthDst(deviceConfig.getDeviceMac(d))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(ns.getEdgeLabel());
|
||||
buckets.add(DefaultGroupBucket.createSelectGroupBucket(
|
||||
tBuilder.build()));
|
||||
}
|
||||
}
|
||||
GroupBuckets groupBuckets = new GroupBuckets(buckets);
|
||||
GroupDescription newGroupDesc = new DefaultGroupDescription(
|
||||
deviceId,
|
||||
Group.Type.SELECT,
|
||||
groupBuckets,
|
||||
ns,
|
||||
appId);
|
||||
log.debug("createGroupsFromNeighborsets: "
|
||||
+ "groupService.addGroup for neighborset{}", ns);
|
||||
groupService.addGroup(newGroupDesc);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleGroupEvent(GroupEvent event) {
|
||||
switch (event.type()) {
|
||||
case GROUP_ADDED:
|
||||
log.debug("Received GROUP_ADDED from group service "
|
||||
+ "for device {} with group key{} with id{}",
|
||||
event.subject().deviceId(),
|
||||
event.subject().appCookie(),
|
||||
event.subject().id());
|
||||
break;
|
||||
case GROUP_UPDATED:
|
||||
log.trace("Received GROUP_UPDATED from group service "
|
||||
+ "for device {} with group key{} with id{}",
|
||||
event.subject().deviceId(),
|
||||
event.subject().appCookie(),
|
||||
event.subject().id());
|
||||
break;
|
||||
case GROUP_REMOVED:
|
||||
log.debug("Received GROUP_REMOVED from group service "
|
||||
+ "for device {} with group key{} with id{}",
|
||||
event.subject().deviceId(),
|
||||
event.subject().appCookie(),
|
||||
event.subject().id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalGroupListener implements GroupListener {
|
||||
|
||||
@Override
|
||||
public void event(GroupEvent event) {
|
||||
handleGroupEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
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.onosproject.core.ApplicationId;
|
||||
import org.onosproject.core.CoreService;
|
||||
import org.onosproject.net.Device;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.device.DeviceEvent;
|
||||
import org.onosproject.net.device.DeviceListener;
|
||||
import org.onosproject.net.device.DeviceService;
|
||||
import org.onosproject.net.group.GroupService;
|
||||
import org.onosproject.net.link.LinkEvent;
|
||||
import org.onosproject.net.link.LinkListener;
|
||||
import org.onosproject.net.link.LinkService;
|
||||
import org.onosproject.net.topology.TopologyService;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Sample application to verify group subsystem end to end.
|
||||
* This application expects a network of maximum of six connected
|
||||
* devices for the test to work. For every device in the network,
|
||||
* this test application launches a default group handler function
|
||||
* that creates ECMP groups for every neighbor the device is
|
||||
* connected to.
|
||||
*/
|
||||
@Component(immediate = true)
|
||||
public class DefaultGroupHandlerApp {
|
||||
|
||||
private final Logger log = getLogger(getClass());
|
||||
|
||||
private final DeviceProperties config = new DeviceConfiguration();
|
||||
private ApplicationId appId;
|
||||
private HashMap<DeviceId, DefaultGroupHandler> dghMap =
|
||||
new HashMap<DeviceId, DefaultGroupHandler>();
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected TopologyService topologyService;
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected DeviceService deviceService;
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected LinkService linkService;
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected GroupService groupService;
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected CoreService coreService;
|
||||
|
||||
private DeviceListener deviceListener = new InternalDeviceListener();
|
||||
private LinkListener linkListener = new InternalLinkListener();
|
||||
|
||||
@Activate
|
||||
public void activate() {
|
||||
appId = coreService.registerApplication("org.onosproject.defaultgrouphandler");
|
||||
log.info("DefaultGroupHandlerApp Activating");
|
||||
deviceService.addListener(deviceListener);
|
||||
linkService.addListener(linkListener);
|
||||
for (Device device: deviceService.getDevices()) {
|
||||
log.debug("Initiating default group handling for {}", device.id());
|
||||
DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(device.id(),
|
||||
appId,
|
||||
config,
|
||||
linkService,
|
||||
groupService);
|
||||
dgh.createGroups();
|
||||
dghMap.put(device.id(), dgh);
|
||||
}
|
||||
log.info("Activated");
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
dghMap.clear();
|
||||
}
|
||||
|
||||
public class DeviceConfiguration implements DeviceProperties {
|
||||
private final List<Integer> allSegmentIds =
|
||||
Arrays.asList(101, 102, 103, 104, 105, 106);
|
||||
private HashMap<DeviceId, Integer> deviceSegmentIdMap =
|
||||
new HashMap<DeviceId, Integer>() {
|
||||
{
|
||||
put(DeviceId.deviceId("of:0000000000000001"), 101);
|
||||
put(DeviceId.deviceId("of:0000000000000002"), 102);
|
||||
put(DeviceId.deviceId("of:0000000000000003"), 103);
|
||||
put(DeviceId.deviceId("of:0000000000000004"), 104);
|
||||
put(DeviceId.deviceId("of:0000000000000005"), 105);
|
||||
put(DeviceId.deviceId("of:0000000000000006"), 106);
|
||||
}
|
||||
};
|
||||
private final HashMap<DeviceId, MacAddress> deviceMacMap =
|
||||
new HashMap<DeviceId, MacAddress>() {
|
||||
{
|
||||
put(DeviceId.deviceId("of:0000000000000001"),
|
||||
MacAddress.valueOf("00:00:00:00:00:01"));
|
||||
put(DeviceId.deviceId("of:0000000000000002"),
|
||||
MacAddress.valueOf("00:00:00:00:00:02"));
|
||||
put(DeviceId.deviceId("of:0000000000000003"),
|
||||
MacAddress.valueOf("00:00:00:00:00:03"));
|
||||
put(DeviceId.deviceId("of:0000000000000004"),
|
||||
MacAddress.valueOf("00:00:00:00:00:04"));
|
||||
put(DeviceId.deviceId("of:0000000000000005"),
|
||||
MacAddress.valueOf("00:00:00:00:00:05"));
|
||||
put(DeviceId.deviceId("of:0000000000000006"),
|
||||
MacAddress.valueOf("00:00:00:00:00:06"));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getSegmentId(DeviceId deviceId) {
|
||||
if (deviceSegmentIdMap.get(deviceId) != null) {
|
||||
log.debug("getSegmentId for device{} is {}",
|
||||
deviceId,
|
||||
deviceSegmentIdMap.get(deviceId));
|
||||
return deviceSegmentIdMap.get(deviceId);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public MacAddress getDeviceMac(DeviceId deviceId) {
|
||||
if (deviceMacMap.get(deviceId) != null) {
|
||||
log.debug("getDeviceMac for device{} is {}",
|
||||
deviceId,
|
||||
deviceMacMap.get(deviceId));
|
||||
return deviceMacMap.get(deviceId);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean isEdgeDevice(DeviceId deviceId) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public List<Integer> getAllDeviceSegmentIds() {
|
||||
return allSegmentIds;
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalDeviceListener implements DeviceListener {
|
||||
|
||||
@Override
|
||||
public void event(DeviceEvent event) {
|
||||
switch (event.type()) {
|
||||
case DEVICE_ADDED:
|
||||
log.debug("Initiating default group handling for {}", event.subject().id());
|
||||
DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(
|
||||
event.subject().id(),
|
||||
appId,
|
||||
config,
|
||||
linkService,
|
||||
groupService);
|
||||
dgh.createGroups();
|
||||
dghMap.put(event.subject().id(), dgh);
|
||||
break;
|
||||
case PORT_REMOVED:
|
||||
if (dghMap.get(event.subject().id()) != null) {
|
||||
dghMap.get(event.subject().id()).portDown(event.port().number());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalLinkListener implements LinkListener {
|
||||
|
||||
@Override
|
||||
public void event(LinkEvent event) {
|
||||
switch (event.type()) {
|
||||
case LINK_ADDED:
|
||||
if (dghMap.get(event.subject().src().deviceId()) != null) {
|
||||
dghMap.get(event.subject().src().deviceId()).linkUp(event.subject());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.Link;
|
||||
import org.onosproject.net.flow.DefaultTrafficTreatment;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
import org.onosproject.net.group.DefaultGroupBucket;
|
||||
import org.onosproject.net.group.GroupBucket;
|
||||
import org.onosproject.net.group.GroupBuckets;
|
||||
import org.onosproject.net.group.GroupService;
|
||||
import org.onosproject.net.link.LinkService;
|
||||
|
||||
/**
|
||||
* Default ECMP group handler creation module for a transit device.
|
||||
* This component creates a set of ECMP groups for every neighbor
|
||||
* that this device is connected to.
|
||||
* For example, consider a network of 4 devices: D0 (Segment ID: 100),
|
||||
* D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
|
||||
* where D0 and D3 are edge devices and D1 and D2 are transit devices.
|
||||
* Assume transit device D1 is connected to 2 neighbors (D0 and D3 ).
|
||||
* The following groups will be created in D1:
|
||||
* 1) all ports to D0 + with no label push,
|
||||
* 2) all ports to D3 + with no label push,
|
||||
*/
|
||||
public class DefaultTransitGroupHandler extends DefaultGroupHandler {
|
||||
|
||||
protected DefaultTransitGroupHandler(DeviceId deviceId,
|
||||
ApplicationId appId,
|
||||
DeviceProperties config,
|
||||
LinkService linkService,
|
||||
GroupService groupService) {
|
||||
super(deviceId, appId, config, linkService, groupService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGroups() {
|
||||
Set<DeviceId> neighbors = devicePortMap.keySet();
|
||||
if (neighbors == null || neighbors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create all possible Neighbor sets from this router
|
||||
// NOTE: Avoid any pairings of edge routers only
|
||||
Set<Set<DeviceId>> sets = getPowerSetOfNeighbors(neighbors);
|
||||
sets = filterEdgeRouterOnlyPairings(sets);
|
||||
log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
|
||||
+ "for sw {} is {}", deviceId, sets.size());
|
||||
Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
|
||||
for (Set<DeviceId> combo : sets) {
|
||||
if (combo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
NeighborSet ns = new NeighborSet(combo);
|
||||
log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
|
||||
deviceId, combo, ns);
|
||||
nsSet.add(ns);
|
||||
}
|
||||
log.debug("createGroupsAtTransitRouter: The neighborset with label "
|
||||
+ "for sw {} is {}", deviceId, nsSet);
|
||||
|
||||
createGroupsFromNeighborsets(nsSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void newNeighbor(Link newNeighborLink) {
|
||||
log.debug("New Neighbor: Updating groups for "
|
||||
+ "transit device {}", deviceId);
|
||||
// Recompute neighbor power set
|
||||
addNeighborAtPort(newNeighborLink.dst().deviceId(),
|
||||
newNeighborLink.src().port());
|
||||
// Compute new neighbor sets due to the addition of new neighbor
|
||||
Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
|
||||
newNeighborLink.dst().deviceId(),
|
||||
devicePortMap.keySet());
|
||||
createGroupsFromNeighborsets(nsSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void newPortToExistingNeighbor(Link newNeighborLink) {
|
||||
log.debug("New port to existing neighbor: Updating "
|
||||
+ "groups for transit device {}", deviceId);
|
||||
addNeighborAtPort(newNeighborLink.dst().deviceId(),
|
||||
newNeighborLink.src().port());
|
||||
Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
|
||||
newNeighborLink.dst().deviceId(),
|
||||
devicePortMap.keySet());
|
||||
for (NeighborSet ns : nsSet) {
|
||||
// Create the new bucket to be updated
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(newNeighborLink.src().port())
|
||||
.setEthDst(deviceConfig.getDeviceMac(
|
||||
newNeighborLink.dst().deviceId()))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(ns.getEdgeLabel());
|
||||
GroupBucket updatedBucket = DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build());
|
||||
GroupBuckets updatedBuckets = new GroupBuckets(
|
||||
Arrays.asList(updatedBucket));
|
||||
log.debug("newPortToExistingNeighborAtEdgeRouter: "
|
||||
+ "groupService.addBucketsToGroup for neighborset{}", ns);
|
||||
groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
|
||||
DeviceId impactedNeighbor,
|
||||
Set<DeviceId> updatedNeighbors) {
|
||||
Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
|
||||
|
||||
Set<DeviceId> tmp = updatedNeighbors;
|
||||
tmp.remove(impactedNeighbor);
|
||||
Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
|
||||
|
||||
// Compute the impacted neighbor sets
|
||||
powerSet.removeAll(tmpPowerSet);
|
||||
|
||||
powerSet = filterEdgeRouterOnlyPairings(powerSet);
|
||||
Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
|
||||
for (Set<DeviceId> combo : powerSet) {
|
||||
if (combo.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
NeighborSet ns = new NeighborSet(combo);
|
||||
log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
|
||||
deviceId, combo, ns);
|
||||
nsSet.add(ns);
|
||||
}
|
||||
log.debug("computeImpactedNeighborsetForPortEvent: The neighborset with label "
|
||||
+ "for sw {} is {}", deviceId, nsSet);
|
||||
|
||||
return nsSet;
|
||||
}
|
||||
|
||||
private Set<Set<DeviceId>> filterEdgeRouterOnlyPairings(Set<Set<DeviceId>> sets) {
|
||||
Set<Set<DeviceId>> fiteredSets = new HashSet<Set<DeviceId>>();
|
||||
for (Set<DeviceId> deviceSubSet : sets) {
|
||||
if (deviceSubSet.size() > 1) {
|
||||
boolean avoidEdgeRouterPairing = true;
|
||||
for (DeviceId device : deviceSubSet) {
|
||||
if (!deviceConfig.isEdgeDevice(device)) {
|
||||
avoidEdgeRouterPairing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!avoidEdgeRouterPairing) {
|
||||
fiteredSets.add(deviceSubSet);
|
||||
}
|
||||
} else {
|
||||
fiteredSets.add(deviceSubSet);
|
||||
}
|
||||
}
|
||||
return fiteredSets;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.onlab.packet.MacAddress;
|
||||
import org.onosproject.net.DeviceId;
|
||||
|
||||
/**
|
||||
* Mechanism through which group handler module retrieves
|
||||
* the device specific attributes such as segment ID,
|
||||
* Mac address...etc from group handler applications.
|
||||
*/
|
||||
public interface DeviceProperties {
|
||||
/**
|
||||
* Returns the segment id of a device to be used in group creation.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @return segment id of a device
|
||||
*/
|
||||
int getSegmentId(DeviceId deviceId);
|
||||
/**
|
||||
* Returns the Mac address of a device to be used in group creation.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @return mac address of a device
|
||||
*/
|
||||
MacAddress getDeviceMac(DeviceId deviceId);
|
||||
/**
|
||||
* Indicates whether a device is edge device or transit/core device.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @return boolean
|
||||
*/
|
||||
boolean isEdgeDevice(DeviceId deviceId);
|
||||
/**
|
||||
* Returns all segment IDs to be considered in building auto
|
||||
*
|
||||
* created groups.
|
||||
* @return list of segment IDs
|
||||
*/
|
||||
List<Integer> getAllDeviceSegmentIds();
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
|
||||
/**
|
||||
* Representation of policy group bucket identifier. Not exposed to
|
||||
* the application and only to be used internally.
|
||||
*/
|
||||
public class GroupBucketIdentifier {
|
||||
private int label;
|
||||
private BucketOutputType type;
|
||||
private PortNumber outPort;
|
||||
private GroupKey outGroup;
|
||||
|
||||
protected enum BucketOutputType {
|
||||
PORT,
|
||||
GROUP
|
||||
}
|
||||
|
||||
protected GroupBucketIdentifier(int label,
|
||||
PortNumber outPort) {
|
||||
this.label = label;
|
||||
this.type = BucketOutputType.PORT;
|
||||
this.outPort = checkNotNull(outPort);
|
||||
this.outGroup = null;
|
||||
}
|
||||
|
||||
protected GroupBucketIdentifier(int label,
|
||||
GroupKey outGroup) {
|
||||
this.label = label;
|
||||
this.type = BucketOutputType.GROUP;
|
||||
this.outPort = null;
|
||||
this.outGroup = checkNotNull(outGroup);
|
||||
}
|
||||
|
||||
protected int label() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
protected BucketOutputType type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
protected PortNumber outPort() {
|
||||
return this.outPort;
|
||||
}
|
||||
|
||||
protected GroupKey outGroup() {
|
||||
return this.outGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
|
||||
/**
|
||||
* Representation of a set of neighbor switch dpids along with edge node
|
||||
* label. Meant to be used as a lookup-key in a hash-map to retrieve an
|
||||
* ECMP-group that hashes packets to a set of ports connecting to the
|
||||
* neighbors in this set.
|
||||
*/
|
||||
public class NeighborSet implements GroupKey {
|
||||
private final Set<DeviceId> neighbors;
|
||||
private final int edgeLabel;
|
||||
|
||||
/**
|
||||
* Constructor with set of neighbors. Edge label is
|
||||
* default to -1.
|
||||
*
|
||||
* @param neighbors set of neighbors to be part of neighbor set
|
||||
*/
|
||||
public NeighborSet(Set<DeviceId> neighbors) {
|
||||
checkNotNull(neighbors);
|
||||
this.edgeLabel = -1;
|
||||
this.neighbors = new HashSet<DeviceId>();
|
||||
this.neighbors.addAll(neighbors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with set of neighbors and edge label.
|
||||
*
|
||||
* @param neighbors set of neighbors to be part of neighbor set
|
||||
* @param edgeLabel label to be pushed as part of group operation
|
||||
*/
|
||||
public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
|
||||
checkNotNull(neighbors);
|
||||
this.edgeLabel = edgeLabel;
|
||||
this.neighbors = new HashSet<DeviceId>();
|
||||
this.neighbors.addAll(neighbors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for kryo serialization.
|
||||
*/
|
||||
public NeighborSet() {
|
||||
this.edgeLabel = -1;
|
||||
this.neighbors = new HashSet<DeviceId>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the neighbors part of neighbor set.
|
||||
*
|
||||
* @return set of neighbor identifiers
|
||||
*/
|
||||
public Set<DeviceId> getDeviceIds() {
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label associated with neighbor set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public int getEdgeLabel() {
|
||||
return edgeLabel;
|
||||
}
|
||||
|
||||
// The list of neighbor ids and label are used for comparison.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof NeighborSet)) {
|
||||
return false;
|
||||
}
|
||||
NeighborSet that = (NeighborSet) o;
|
||||
return (this.neighbors.containsAll(that.neighbors) &&
|
||||
that.neighbors.containsAll(this.neighbors) &&
|
||||
(this.edgeLabel == that.edgeLabel));
|
||||
}
|
||||
|
||||
// The list of neighbor ids and label are used for comparison.
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
int combinedHash = 0;
|
||||
for (DeviceId d : neighbors) {
|
||||
combinedHash = combinedHash + Objects.hash(d);
|
||||
}
|
||||
result = 31 * result + combinedHash + Objects.hash(edgeLabel);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " Neighborset Sw: " + neighbors
|
||||
+ " and Label: " + edgeLabel;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.core.GroupId;
|
||||
import org.onosproject.grouphandler.GroupBucketIdentifier.BucketOutputType;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.flow.DefaultTrafficTreatment;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
import org.onosproject.net.group.DefaultGroupBucket;
|
||||
import org.onosproject.net.group.DefaultGroupDescription;
|
||||
import org.onosproject.net.group.GroupBucket;
|
||||
import org.onosproject.net.group.GroupBuckets;
|
||||
import org.onosproject.net.group.GroupDescription;
|
||||
import org.onosproject.net.group.GroupEvent;
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
import org.onosproject.net.group.GroupService;
|
||||
import org.onosproject.net.link.LinkService;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* A module to create group chains based on the specified device
|
||||
* ports and label stack to be applied on each port.
|
||||
*/
|
||||
public class PolicyGroupHandler extends DefaultGroupHandler {
|
||||
|
||||
private final Logger log = getLogger(getClass());
|
||||
private HashMap<GroupKey, GroupKey> dependentGroups =
|
||||
new HashMap<GroupKey, GroupKey>();
|
||||
|
||||
/**
|
||||
* Creates policy group handler object.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @param appId application identifier
|
||||
* @param config interface to retrieve the device properties
|
||||
* @param linkService link service object
|
||||
* @param groupService group service object
|
||||
* @return policy group handler type
|
||||
*/
|
||||
public PolicyGroupHandler(DeviceId deviceId,
|
||||
ApplicationId appId,
|
||||
DeviceProperties config,
|
||||
LinkService linkService,
|
||||
GroupService groupService) {
|
||||
super(deviceId, appId, config, linkService, groupService);
|
||||
}
|
||||
|
||||
public PolicyGroupIdentifier createPolicyGroupChain(String id,
|
||||
List<PolicyGroupParams> params) {
|
||||
List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
|
||||
for (PolicyGroupParams param: params) {
|
||||
List<PortNumber> ports = param.getPorts();
|
||||
if (ports == null) {
|
||||
log.warn("createPolicyGroupChain in sw {} with wrong "
|
||||
+ "input parameters", deviceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
int labelStackSize = (param.getLabelStack() != null) ?
|
||||
param.getLabelStack().size() : 0;
|
||||
|
||||
if (labelStackSize > 1) {
|
||||
for (PortNumber sp : ports) {
|
||||
PolicyGroupIdentifier previousGroupkey = null;
|
||||
DeviceId neighbor = portDeviceMap.get(sp);
|
||||
for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
|
||||
int label = param.getLabelStack().get(idx).intValue();
|
||||
if (idx == (labelStackSize - 1)) {
|
||||
// Innermost Group
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label,
|
||||
previousGroupkey);
|
||||
bucketIds.add(bucketId);
|
||||
} else if (idx == 0) {
|
||||
// Outermost Group
|
||||
List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label, sp);
|
||||
PolicyGroupIdentifier key = new
|
||||
PolicyGroupIdentifier(id,
|
||||
Arrays.asList(param),
|
||||
Arrays.asList(bucketId));
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(sp)
|
||||
.setEthDst(deviceConfig.
|
||||
getDeviceMac(neighbor))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(label);
|
||||
outBuckets.add(DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build()));
|
||||
GroupDescription desc = new
|
||||
DefaultGroupDescription(deviceId,
|
||||
GroupDescription.Type.INDIRECT,
|
||||
new GroupBuckets(outBuckets));
|
||||
//TODO: BoS
|
||||
previousGroupkey = key;
|
||||
groupService.addGroup(desc);
|
||||
} else {
|
||||
// Intermediate Groups
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label,
|
||||
previousGroupkey);
|
||||
PolicyGroupIdentifier key = new
|
||||
PolicyGroupIdentifier(id,
|
||||
Arrays.asList(param),
|
||||
Arrays.asList(bucketId));
|
||||
// Add to group dependency list
|
||||
dependentGroups.put(previousGroupkey, key);
|
||||
previousGroupkey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int label = -1;
|
||||
if (labelStackSize == 1) {
|
||||
label = param.getLabelStack().get(0).intValue();
|
||||
}
|
||||
for (PortNumber sp : ports) {
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label, sp);
|
||||
bucketIds.add(bucketId);
|
||||
}
|
||||
}
|
||||
}
|
||||
PolicyGroupIdentifier innermostGroupkey = null;
|
||||
if (!bucketIds.isEmpty()) {
|
||||
innermostGroupkey = new
|
||||
PolicyGroupIdentifier(id,
|
||||
params,
|
||||
bucketIds);
|
||||
// Add to group dependency list
|
||||
boolean fullyResolved = true;
|
||||
for (GroupBucketIdentifier bucketId:bucketIds) {
|
||||
if (bucketId.type() == BucketOutputType.GROUP) {
|
||||
dependentGroups.put(bucketId.outGroup(),
|
||||
innermostGroupkey);
|
||||
fullyResolved = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullyResolved) {
|
||||
List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
|
||||
for (GroupBucketIdentifier bucketId:bucketIds) {
|
||||
DeviceId neighbor = portDeviceMap.
|
||||
get(bucketId.outPort());
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(bucketId.outPort())
|
||||
.setEthDst(deviceConfig.
|
||||
getDeviceMac(neighbor))
|
||||
.setEthSrc(nodeMacAddr)
|
||||
.pushMpls()
|
||||
.setMpls(bucketId.label());
|
||||
//TODO: BoS
|
||||
outBuckets.add(DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build()));
|
||||
}
|
||||
GroupDescription desc = new
|
||||
DefaultGroupDescription(deviceId,
|
||||
GroupDescription.Type.SELECT,
|
||||
new GroupBuckets(outBuckets));
|
||||
groupService.addGroup(desc);
|
||||
}
|
||||
}
|
||||
return innermostGroupkey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleGroupEvent(GroupEvent event) {
|
||||
if (event.type() == GroupEvent.Type.GROUP_ADDED) {
|
||||
if (dependentGroups.get(event.subject().appCookie()) != null) {
|
||||
PolicyGroupIdentifier dependentGroupKey = (PolicyGroupIdentifier)
|
||||
dependentGroups.get(event.subject().appCookie());
|
||||
dependentGroups.remove(event.subject().appCookie());
|
||||
boolean fullyResolved = true;
|
||||
for (GroupBucketIdentifier bucketId:
|
||||
dependentGroupKey.bucketIds()) {
|
||||
if (bucketId.type() != BucketOutputType.GROUP) {
|
||||
continue;
|
||||
}
|
||||
if (dependentGroups.containsKey(bucketId.outGroup())) {
|
||||
fullyResolved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullyResolved) {
|
||||
List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
|
||||
for (GroupBucketIdentifier bucketId:
|
||||
dependentGroupKey.bucketIds()) {
|
||||
TrafficTreatment.Builder tBuilder =
|
||||
DefaultTrafficTreatment.builder();
|
||||
tBuilder.pushMpls()
|
||||
.setMpls(bucketId.label());
|
||||
//TODO: BoS
|
||||
if (bucketId.type() == BucketOutputType.PORT) {
|
||||
DeviceId neighbor = portDeviceMap.
|
||||
get(bucketId.outPort());
|
||||
tBuilder.setOutput(bucketId.outPort())
|
||||
.setEthDst(deviceConfig.
|
||||
getDeviceMac(neighbor))
|
||||
.setEthSrc(nodeMacAddr);
|
||||
} else {
|
||||
if (groupService.
|
||||
getGroup(deviceId,
|
||||
bucketId.outGroup()) == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
GroupId indirectGroupId = groupService.
|
||||
getGroup(deviceId,
|
||||
bucketId.outGroup()).id();
|
||||
tBuilder.group(indirectGroupId);
|
||||
}
|
||||
outBuckets.add(DefaultGroupBucket.
|
||||
createSelectGroupBucket(tBuilder.build()));
|
||||
}
|
||||
GroupDescription desc = new
|
||||
DefaultGroupDescription(deviceId,
|
||||
GroupDescription.Type.SELECT,
|
||||
new GroupBuckets(outBuckets));
|
||||
groupService.addGroup(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GroupKey generatePolicyGroupKey(String id,
|
||||
List<PolicyGroupParams> params) {
|
||||
List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
|
||||
for (PolicyGroupParams param: params) {
|
||||
List<PortNumber> ports = param.getPorts();
|
||||
if (ports == null) {
|
||||
log.warn("generateGroupKey in sw {} with wrong "
|
||||
+ "input parameters", deviceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
int labelStackSize = (param.getLabelStack() != null)
|
||||
? param.getLabelStack().size() : 0;
|
||||
|
||||
if (labelStackSize > 1) {
|
||||
for (PortNumber sp : ports) {
|
||||
PolicyGroupIdentifier previousGroupkey = null;
|
||||
for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
|
||||
int label = param.getLabelStack().get(idx).intValue();
|
||||
if (idx == (labelStackSize - 1)) {
|
||||
// Innermost Group
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label,
|
||||
previousGroupkey);
|
||||
bucketIds.add(bucketId);
|
||||
} else if (idx == 0) {
|
||||
// Outermost Group
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label, sp);
|
||||
PolicyGroupIdentifier key = new
|
||||
PolicyGroupIdentifier(id,
|
||||
Arrays.asList(param),
|
||||
Arrays.asList(bucketId));
|
||||
previousGroupkey = key;
|
||||
} else {
|
||||
// Intermediate Groups
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label,
|
||||
previousGroupkey);
|
||||
PolicyGroupIdentifier key = new
|
||||
PolicyGroupIdentifier(id,
|
||||
Arrays.asList(param),
|
||||
Arrays.asList(bucketId));
|
||||
previousGroupkey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int label = -1;
|
||||
if (labelStackSize == 1) {
|
||||
label = param.getLabelStack().get(0).intValue();
|
||||
}
|
||||
for (PortNumber sp : ports) {
|
||||
GroupBucketIdentifier bucketId =
|
||||
new GroupBucketIdentifier(label, sp);
|
||||
bucketIds.add(bucketId);
|
||||
}
|
||||
}
|
||||
}
|
||||
PolicyGroupIdentifier innermostGroupkey = null;
|
||||
if (!bucketIds.isEmpty()) {
|
||||
innermostGroupkey = new
|
||||
PolicyGroupIdentifier(id,
|
||||
params,
|
||||
bucketIds);
|
||||
}
|
||||
return innermostGroupkey;
|
||||
}
|
||||
|
||||
public void removeGroupChain(GroupKey key) {
|
||||
if (!(key instanceof PolicyGroupIdentifier)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
List<GroupKey> groupsToBeDeleted = new ArrayList<GroupKey>();
|
||||
groupsToBeDeleted.add(key);
|
||||
|
||||
Iterator<GroupKey> it = groupsToBeDeleted.iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
PolicyGroupIdentifier innerMostGroupKey =
|
||||
(PolicyGroupIdentifier) it.next();
|
||||
for (GroupBucketIdentifier bucketId:
|
||||
innerMostGroupKey.bucketIds()) {
|
||||
if (bucketId.type() != BucketOutputType.GROUP) {
|
||||
groupsToBeDeleted.add(bucketId.outGroup());
|
||||
}
|
||||
}
|
||||
groupService.removeGroup(deviceId, innerMostGroupKey, appId);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.onosproject.net.group.GroupKey;
|
||||
|
||||
/**
|
||||
* Representation of policy based group identifiers.
|
||||
* Opaque to group handler applications and only the outermost
|
||||
* policy group identifier in a chain is visible to the applications.
|
||||
*/
|
||||
public class PolicyGroupIdentifier implements GroupKey {
|
||||
private String id;
|
||||
private List<PolicyGroupParams> inputParams;
|
||||
private List<GroupBucketIdentifier> bucketIds;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id unique identifier associated with the policy group
|
||||
* @param input policy group params associated with this group
|
||||
* @param bucketIds buckets associated with this group
|
||||
*/
|
||||
protected PolicyGroupIdentifier(String id,
|
||||
List<PolicyGroupParams> input,
|
||||
List<GroupBucketIdentifier> bucketIds) {
|
||||
this.id = id;
|
||||
this.inputParams = input;
|
||||
this.bucketIds = bucketIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bucket identifier list associated with the policy
|
||||
* group identifier.
|
||||
*
|
||||
* @return list of bucket identifier
|
||||
*/
|
||||
protected List<GroupBucketIdentifier> bucketIds() {
|
||||
return this.bucketIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
int combinedHash = 0;
|
||||
for (PolicyGroupParams input:inputParams) {
|
||||
combinedHash = combinedHash + input.hashCode();
|
||||
}
|
||||
for (GroupBucketIdentifier bucketId:bucketIds) {
|
||||
combinedHash = combinedHash + bucketId.hashCode();
|
||||
}
|
||||
result = 31 * result + combinedHash;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof PolicyGroupIdentifier) {
|
||||
PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
|
||||
boolean result = this.id.equals(that.id);
|
||||
result = result &&
|
||||
this.inputParams.containsAll(that.inputParams) &&
|
||||
that.inputParams.containsAll(this.inputParams);
|
||||
result = result &&
|
||||
this.bucketIds.containsAll(that.bucketIds) &&
|
||||
that.bucketIds.containsAll(this.bucketIds);
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2015 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.grouphandler;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.onosproject.net.PortNumber;
|
||||
|
||||
/**
|
||||
* Representation of parameters used to create policy based groups.
|
||||
*/
|
||||
public class PolicyGroupParams {
|
||||
private final List<PortNumber> ports;
|
||||
private final List<Integer> labelStack;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param labelStack mpls label stack to be applied on the ports
|
||||
* @param ports ports to be part of the policy group
|
||||
*/
|
||||
public PolicyGroupParams(List<Integer> labelStack,
|
||||
List<PortNumber> ports) {
|
||||
this.ports = checkNotNull(ports);
|
||||
this.labelStack = checkNotNull(labelStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ports associated with the policy group params.
|
||||
*
|
||||
* @return list of port numbers
|
||||
*/
|
||||
public List<PortNumber> getPorts() {
|
||||
return ports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label stack associated with the policy group params.
|
||||
*
|
||||
* @return list of integers
|
||||
*/
|
||||
public List<Integer> getLabelStack() {
|
||||
return labelStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
int combinedHash = 0;
|
||||
for (PortNumber port:ports) {
|
||||
combinedHash = combinedHash + port.hashCode();
|
||||
}
|
||||
combinedHash = combinedHash + Objects.hash(labelStack);
|
||||
result = 31 * result + combinedHash;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof PolicyGroupParams) {
|
||||
PolicyGroupParams that = (PolicyGroupParams) obj;
|
||||
boolean result = this.labelStack.equals(that.labelStack);
|
||||
result = result &&
|
||||
this.ports.containsAll(that.ports) &&
|
||||
that.ports.containsAll(this.ports);
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.onosproject.net.group;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -205,4 +206,13 @@ public class DefaultGroup extends DefaultGroupDescription
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this)
|
||||
.add("description", super.toString())
|
||||
.add("groupid", id)
|
||||
.add("state", state)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.onosproject.net.group;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -212,4 +213,11 @@ public final class DefaultGroupBucket implements GroupBucket {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this)
|
||||
.add("type", type)
|
||||
.add("treatment", treatment)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.onosproject.net.group;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -168,4 +169,13 @@ public class DefaultGroupDescription implements GroupDescription {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this)
|
||||
.add("deviceId", deviceId)
|
||||
.add("type", type)
|
||||
.add("buckets", buckets)
|
||||
.add("appId", appId)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.onosproject.net.group;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
@ -66,4 +67,10 @@ public final class GroupBuckets {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this)
|
||||
.add("buckets", buckets)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -132,8 +132,9 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
|
||||
* Indicates the first group audit is completed.
|
||||
*
|
||||
* @param deviceId the device ID
|
||||
* @param completed initial audit status
|
||||
*/
|
||||
void deviceInitialAuditCompleted(DeviceId deviceId);
|
||||
void deviceInitialAuditCompleted(DeviceId deviceId, boolean completed);
|
||||
|
||||
/**
|
||||
* Retrieves the initial group audit status for a device.
|
||||
|
||||
@ -32,6 +32,9 @@ import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.event.AbstractListenerRegistry;
|
||||
import org.onosproject.event.EventDeliveryService;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.device.DeviceEvent;
|
||||
import org.onosproject.net.device.DeviceListener;
|
||||
import org.onosproject.net.device.DeviceService;
|
||||
import org.onosproject.net.group.Group;
|
||||
import org.onosproject.net.group.GroupBuckets;
|
||||
import org.onosproject.net.group.GroupDescription;
|
||||
@ -67,10 +70,14 @@ public class GroupManager
|
||||
private final AbstractListenerRegistry<GroupEvent, GroupListener>
|
||||
listenerRegistry = new AbstractListenerRegistry<>();
|
||||
private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
|
||||
private final DeviceListener deviceListener = new InternalDeviceListener();
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected GroupStore store;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected DeviceService deviceService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
protected EventDeliveryService eventDispatcher;
|
||||
|
||||
@ -78,6 +85,7 @@ public class GroupManager
|
||||
public void activate() {
|
||||
store.setDelegate(delegate);
|
||||
eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
|
||||
deviceService.addListener(deviceListener);
|
||||
log.info("Started");
|
||||
}
|
||||
|
||||
@ -232,6 +240,8 @@ public class GroupManager
|
||||
GroupOperations groupOps = null;
|
||||
switch (event.type()) {
|
||||
case GROUP_ADD_REQUESTED:
|
||||
log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
|
||||
group.id(), group.deviceId());
|
||||
GroupOperation groupAddOp = GroupOperation.
|
||||
createAddGroupOperation(group.id(),
|
||||
group.type(),
|
||||
@ -242,6 +252,8 @@ public class GroupManager
|
||||
break;
|
||||
|
||||
case GROUP_UPDATE_REQUESTED:
|
||||
log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
|
||||
group.id(), group.deviceId());
|
||||
GroupOperation groupModifyOp = GroupOperation.
|
||||
createModifyGroupOperation(group.id(),
|
||||
group.type(),
|
||||
@ -252,6 +264,8 @@ public class GroupManager
|
||||
break;
|
||||
|
||||
case GROUP_REMOVE_REQUESTED:
|
||||
log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
|
||||
group.id(), group.deviceId());
|
||||
GroupOperation groupDeleteOp = GroupOperation.
|
||||
createDeleteGroupOperation(group.id(),
|
||||
group.type());
|
||||
@ -294,10 +308,14 @@ public class GroupManager
|
||||
GroupProvider gp = getProvider(group.deviceId());
|
||||
switch (group.state()) {
|
||||
case PENDING_DELETE:
|
||||
log.debug("Group {} delete confirmation from device {}",
|
||||
group, group.deviceId());
|
||||
store.removeGroupEntry(group);
|
||||
break;
|
||||
case ADDED:
|
||||
case PENDING_ADD:
|
||||
log.debug("Group {} is in store but not on device {}",
|
||||
group, group.deviceId());
|
||||
GroupOperation groupAddOp = GroupOperation.
|
||||
createAddGroupOperation(group.id(),
|
||||
group.type(),
|
||||
@ -314,7 +332,8 @@ public class GroupManager
|
||||
|
||||
|
||||
private void extraneousGroup(Group group) {
|
||||
log.debug("Group {} is on switch but not in store.", group);
|
||||
log.debug("Group {} is on device {} but not in store.",
|
||||
group, group.deviceId());
|
||||
checkValidity();
|
||||
store.addOrUpdateExtraneousGroupEntry(group);
|
||||
}
|
||||
@ -322,13 +341,16 @@ public class GroupManager
|
||||
private void groupAdded(Group group) {
|
||||
checkValidity();
|
||||
|
||||
log.trace("Group {}", group);
|
||||
log.trace("Group {} Added or Updated in device {}",
|
||||
group, group.deviceId());
|
||||
store.addOrUpdateGroupEntry(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushGroupMetrics(DeviceId deviceId,
|
||||
Collection<Group> groupEntries) {
|
||||
log.trace("Received group metrics from device {}",
|
||||
deviceId);
|
||||
boolean deviceInitialAuditStatus =
|
||||
store.deviceInitialAuditStatus(deviceId);
|
||||
Set<Group> southboundGroupEntries =
|
||||
@ -338,31 +360,75 @@ public class GroupManager
|
||||
Set<Group> extraneousStoredEntries =
|
||||
Sets.newHashSet(store.getExtraneousGroups(deviceId));
|
||||
|
||||
log.trace("Displaying all southboundGroupEntries for device {}", deviceId);
|
||||
for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
|
||||
Group group = it.next();
|
||||
log.trace("Group {} in device {}", group, deviceId);
|
||||
}
|
||||
|
||||
log.trace("Displaying all stored group entries for device {}", deviceId);
|
||||
for (Iterator<Group> it = storedGroupEntries.iterator(); it.hasNext();) {
|
||||
Group group = it.next();
|
||||
log.trace("Stored Group {} for device {}", group, deviceId);
|
||||
}
|
||||
|
||||
for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
|
||||
Group group = it.next();
|
||||
if (storedGroupEntries.remove(group)) {
|
||||
// we both have the group, let's update some info then.
|
||||
log.trace("Group AUDIT: group {} exists "
|
||||
+ "in both planes for device {}",
|
||||
group.id(), deviceId);
|
||||
groupAdded(group);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
for (Group group : southboundGroupEntries) {
|
||||
// there are groups in the switch that aren't in the store
|
||||
log.trace("Group AUDIT: extraneous group {} exists "
|
||||
+ "in data plane for device {}",
|
||||
group.id(), deviceId);
|
||||
extraneousStoredEntries.remove(group);
|
||||
extraneousGroup(group);
|
||||
}
|
||||
for (Group group : storedGroupEntries) {
|
||||
// there are groups in the store that aren't in the switch
|
||||
log.trace("Group AUDIT: group {} missing "
|
||||
+ "in data plane for device {}",
|
||||
group.id(), deviceId);
|
||||
groupMissing(group);
|
||||
}
|
||||
for (Group group : extraneousStoredEntries) {
|
||||
// there are groups in the extraneous store that
|
||||
// aren't in the switch
|
||||
log.trace("Group AUDIT: clearing extransoeus group {} "
|
||||
+ "from store for device {}",
|
||||
group.id(), deviceId);
|
||||
store.removeExtraneousGroupEntry(group);
|
||||
}
|
||||
|
||||
if (!deviceInitialAuditStatus) {
|
||||
store.deviceInitialAuditCompleted(deviceId);
|
||||
log.debug("Group AUDIT: Setting device {} initial "
|
||||
+ "AUDIT completed", deviceId);
|
||||
store.deviceInitialAuditCompleted(deviceId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalDeviceListener implements DeviceListener {
|
||||
|
||||
@Override
|
||||
public void event(DeviceEvent event) {
|
||||
switch (event.type()) {
|
||||
case DEVICE_REMOVED:
|
||||
log.debug("Clearing device {} initial "
|
||||
+ "AUDIT completed status as device is going down",
|
||||
event.subject().id());
|
||||
store.deviceInitialAuditCompleted(event.subject().id(), false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ import org.onosproject.core.GroupId;
|
||||
import org.onosproject.event.impl.TestEventDispatcher;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.device.impl.DeviceManager;
|
||||
import org.onosproject.net.flow.DefaultTrafficTreatment;
|
||||
import org.onosproject.net.flow.TrafficTreatment;
|
||||
import org.onosproject.net.group.DefaultGroup;
|
||||
@ -81,6 +82,7 @@ public class GroupManagerTest {
|
||||
public void setUp() {
|
||||
mgr = new GroupManager();
|
||||
groupService = mgr;
|
||||
mgr.deviceService = new DeviceManager();
|
||||
mgr.store = new SimpleGroupStore();
|
||||
mgr.eventDispatcher = new TestEventDispatcher();
|
||||
providerRegistry = mgr;
|
||||
@ -147,11 +149,34 @@ public class GroupManagerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testGroupService() {
|
||||
// Test Group creation before AUDIT process
|
||||
testGroupCreationBeforeAudit();
|
||||
|
||||
// Test initial group audit process
|
||||
testInitialAuditWithPendingGroupRequests();
|
||||
|
||||
// Test audit with extraneous and missing groups
|
||||
testAuditWithExtraneousMissingGroups();
|
||||
|
||||
// Test audit with confirmed groups
|
||||
testAuditWithConfirmedGroups();
|
||||
|
||||
// Test group add bucket operations
|
||||
testAddBuckets();
|
||||
|
||||
// Test group remove bucket operations
|
||||
testRemoveBuckets();
|
||||
|
||||
// Test group remove operations
|
||||
testRemoveGroup();
|
||||
}
|
||||
|
||||
// Test Group creation before AUDIT process
|
||||
private void testGroupCreationBeforeAudit() {
|
||||
PortNumber[] ports1 = {PortNumber.portNumber(31),
|
||||
PortNumber.portNumber(32)};
|
||||
PortNumber[] ports2 = {PortNumber.portNumber(41),
|
||||
PortNumber.portNumber(42)};
|
||||
// Test Group creation before AUDIT process
|
||||
TestGroupKey key = new TestGroupKey("group1BeforeAudit");
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||
@ -177,8 +202,14 @@ public class GroupManagerTest {
|
||||
internalProvider.validate(DID, null);
|
||||
assertEquals(null, groupService.getGroup(DID, key));
|
||||
assertEquals(0, Iterables.size(groupService.getGroups(DID, appId)));
|
||||
}
|
||||
|
||||
// Test initial group audit process
|
||||
// Test initial AUDIT process with pending group requests
|
||||
private void testInitialAuditWithPendingGroupRequests() {
|
||||
PortNumber[] ports1 = {PortNumber.portNumber(31),
|
||||
PortNumber.portNumber(32)};
|
||||
PortNumber[] ports2 = {PortNumber.portNumber(41),
|
||||
PortNumber.portNumber(42)};
|
||||
GroupId gId1 = new DefaultGroupId(1);
|
||||
Group group1 = createSouthboundGroupEntry(gId1,
|
||||
Arrays.asList(ports1),
|
||||
@ -193,50 +224,76 @@ public class GroupManagerTest {
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
// First group metrics would trigger the device audit completion
|
||||
// post which all pending group requests are also executed.
|
||||
TestGroupKey key = new TestGroupKey("group1BeforeAudit");
|
||||
Group createdGroup = groupService.getGroup(DID, key);
|
||||
int createdGroupId = createdGroup.id().id();
|
||||
assertNotEquals(gId1.id(), createdGroupId);
|
||||
assertNotEquals(gId2.id(), createdGroupId);
|
||||
|
||||
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createDeleteGroupOperation(gId1,
|
||||
Group.Type.SELECT),
|
||||
GroupOperation.createAddGroupOperation(
|
||||
createdGroup.id(),
|
||||
Group.Type.SELECT,
|
||||
groupBuckets));
|
||||
createdGroup.buckets()));
|
||||
internalProvider.validate(DID, expectedGroupOps);
|
||||
}
|
||||
|
||||
group1 = createSouthboundGroupEntry(gId1,
|
||||
// Test AUDIT process with extraneous groups and missing groups
|
||||
private void testAuditWithExtraneousMissingGroups() {
|
||||
PortNumber[] ports1 = {PortNumber.portNumber(31),
|
||||
PortNumber.portNumber(32)};
|
||||
PortNumber[] ports2 = {PortNumber.portNumber(41),
|
||||
PortNumber.portNumber(42)};
|
||||
GroupId gId1 = new DefaultGroupId(1);
|
||||
Group group1 = createSouthboundGroupEntry(gId1,
|
||||
Arrays.asList(ports1),
|
||||
0);
|
||||
group2 = createSouthboundGroupEntry(gId2,
|
||||
GroupId gId2 = new DefaultGroupId(2);
|
||||
Group group2 = createSouthboundGroupEntry(gId2,
|
||||
Arrays.asList(ports2),
|
||||
0);
|
||||
groupEntries = Arrays.asList(group1, group2);
|
||||
List<Group> groupEntries = Arrays.asList(group1, group2);
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
expectedGroupOps = Arrays.asList(
|
||||
TestGroupKey key = new TestGroupKey("group1BeforeAudit");
|
||||
Group createdGroup = groupService.getGroup(DID, key);
|
||||
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createDeleteGroupOperation(gId1,
|
||||
Group.Type.SELECT),
|
||||
GroupOperation.createDeleteGroupOperation(gId2,
|
||||
Group.Type.SELECT),
|
||||
GroupOperation.createAddGroupOperation(createdGroup.id(),
|
||||
Group.Type.SELECT,
|
||||
groupBuckets));
|
||||
createdGroup.buckets()));
|
||||
internalProvider.validate(DID, expectedGroupOps);
|
||||
}
|
||||
|
||||
// Test AUDIT with confirmed groups
|
||||
private void testAuditWithConfirmedGroups() {
|
||||
TestGroupKey key = new TestGroupKey("group1BeforeAudit");
|
||||
Group createdGroup = groupService.getGroup(DID, key);
|
||||
createdGroup = new DefaultGroup(createdGroup.id(),
|
||||
DID,
|
||||
Group.Type.SELECT,
|
||||
groupBuckets);
|
||||
groupEntries = Arrays.asList(createdGroup);
|
||||
createdGroup.buckets());
|
||||
List<Group> groupEntries = Arrays.asList(createdGroup);
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
|
||||
}
|
||||
|
||||
// Test group add bucket operations
|
||||
// Test group add bucket operations
|
||||
private void testAddBuckets() {
|
||||
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
||||
|
||||
TestGroupKey prevKey = new TestGroupKey("group1BeforeAudit");
|
||||
Group createdGroup = groupService.getGroup(DID, prevKey);
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
buckets.addAll(createdGroup.buckets().buckets());
|
||||
|
||||
PortNumber[] addPorts = {PortNumber.portNumber(51),
|
||||
PortNumber.portNumber(52)};
|
||||
outPorts.clear();
|
||||
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||
outPorts.addAll(Arrays.asList(addPorts));
|
||||
List<GroupBucket> addBuckets = new ArrayList<GroupBucket>();
|
||||
for (PortNumber portNumber: outPorts) {
|
||||
@ -253,26 +310,34 @@ public class GroupManagerTest {
|
||||
}
|
||||
GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
|
||||
groupService.addBucketsToGroup(DID,
|
||||
key,
|
||||
prevKey,
|
||||
groupAddBuckets,
|
||||
addKey,
|
||||
appId);
|
||||
GroupBuckets updatedBuckets = new GroupBuckets(buckets);
|
||||
expectedGroupOps = Arrays.asList(
|
||||
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createModifyGroupOperation(createdGroup.id(),
|
||||
Group.Type.SELECT,
|
||||
updatedBuckets));
|
||||
internalProvider.validate(DID, expectedGroupOps);
|
||||
Group existingGroup = groupService.getGroup(DID, addKey);
|
||||
groupEntries = Arrays.asList(existingGroup);
|
||||
List<Group> groupEntries = Arrays.asList(existingGroup);
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
|
||||
}
|
||||
|
||||
// Test group remove bucket operations
|
||||
// Test group remove bucket operations
|
||||
private void testRemoveBuckets() {
|
||||
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
||||
|
||||
TestGroupKey prevKey = new TestGroupKey("group1AddBuckets");
|
||||
Group createdGroup = groupService.getGroup(DID, prevKey);
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
buckets.addAll(createdGroup.buckets().buckets());
|
||||
|
||||
PortNumber[] removePorts = {PortNumber.portNumber(31),
|
||||
PortNumber.portNumber(32)};
|
||||
outPorts.clear();
|
||||
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||
outPorts.addAll(Arrays.asList(removePorts));
|
||||
List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>();
|
||||
for (PortNumber portNumber: outPorts) {
|
||||
@ -289,28 +354,32 @@ public class GroupManagerTest {
|
||||
}
|
||||
GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
|
||||
groupService.removeBucketsFromGroup(DID,
|
||||
addKey,
|
||||
prevKey,
|
||||
groupRemoveBuckets,
|
||||
removeKey,
|
||||
appId);
|
||||
updatedBuckets = new GroupBuckets(buckets);
|
||||
expectedGroupOps = Arrays.asList(
|
||||
GroupBuckets updatedBuckets = new GroupBuckets(buckets);
|
||||
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createModifyGroupOperation(createdGroup.id(),
|
||||
Group.Type.SELECT,
|
||||
updatedBuckets));
|
||||
internalProvider.validate(DID, expectedGroupOps);
|
||||
existingGroup = groupService.getGroup(DID, removeKey);
|
||||
groupEntries = Arrays.asList(existingGroup);
|
||||
Group existingGroup = groupService.getGroup(DID, removeKey);
|
||||
List<Group> groupEntries = Arrays.asList(existingGroup);
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
|
||||
}
|
||||
|
||||
// Test group remove operations
|
||||
groupService.removeGroup(DID, removeKey, appId);
|
||||
expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createDeleteGroupOperation(createdGroup.id(),
|
||||
// Test group remove operations
|
||||
private void testRemoveGroup() {
|
||||
TestGroupKey currKey = new TestGroupKey("group1RemoveBuckets");
|
||||
Group existingGroup = groupService.getGroup(DID, currKey);
|
||||
groupService.removeGroup(DID, currKey, appId);
|
||||
List<GroupOperation> expectedGroupOps = Arrays.asList(
|
||||
GroupOperation.createDeleteGroupOperation(existingGroup.id(),
|
||||
Group.Type.SELECT));
|
||||
internalProvider.validate(DID, expectedGroupOps);
|
||||
groupEntries = Collections.emptyList();
|
||||
List<Group> groupEntries = Collections.emptyList();
|
||||
providerService.pushGroupMetrics(DID, groupEntries);
|
||||
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
|
||||
}
|
||||
|
||||
@ -261,6 +261,11 @@ public class SimpleGroupStore
|
||||
}
|
||||
|
||||
private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
|
||||
// Check if a group is existing with the same key
|
||||
if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a new group identifier
|
||||
GroupId id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
|
||||
// Create a group entry object
|
||||
@ -448,29 +453,41 @@ public class SimpleGroupStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deviceInitialAuditCompleted(DeviceId deviceId) {
|
||||
public void deviceInitialAuditCompleted(DeviceId deviceId,
|
||||
boolean completed) {
|
||||
synchronized (deviceAuditStatus) {
|
||||
deviceAuditStatus.putIfAbsent(deviceId, true);
|
||||
// Execute all pending group requests
|
||||
ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
|
||||
getPendingGroupKeyTable(deviceId);
|
||||
for (Group group:pendingGroupRequests.values()) {
|
||||
GroupDescription tmp = new DefaultGroupDescription(
|
||||
group.deviceId(),
|
||||
group.type(),
|
||||
group.buckets(),
|
||||
group.appCookie(),
|
||||
group.appId());
|
||||
storeGroupDescriptionInternal(tmp);
|
||||
if (completed) {
|
||||
log.debug("deviceInitialAuditCompleted: AUDIT "
|
||||
+ "completed for device {}", deviceId);
|
||||
deviceAuditStatus.put(deviceId, true);
|
||||
// Execute all pending group requests
|
||||
ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
|
||||
getPendingGroupKeyTable(deviceId);
|
||||
for (Group group:pendingGroupRequests.values()) {
|
||||
GroupDescription tmp = new DefaultGroupDescription(
|
||||
group.deviceId(),
|
||||
group.type(),
|
||||
group.buckets(),
|
||||
group.appCookie(),
|
||||
group.appId());
|
||||
storeGroupDescriptionInternal(tmp);
|
||||
}
|
||||
getPendingGroupKeyTable(deviceId).clear();
|
||||
} else {
|
||||
if (deviceAuditStatus.get(deviceId)) {
|
||||
log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
|
||||
+ "status for device {}", deviceId);
|
||||
deviceAuditStatus.put(deviceId, false);
|
||||
}
|
||||
}
|
||||
getPendingGroupKeyTable(deviceId).clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deviceInitialAuditStatus(DeviceId deviceId) {
|
||||
synchronized (deviceAuditStatus) {
|
||||
return (deviceAuditStatus.get(deviceId) != null) ? true : false;
|
||||
return (deviceAuditStatus.get(deviceId) != null)
|
||||
? deviceAuditStatus.get(deviceId) : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.onosproject.net.DeviceId.deviceId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
@ -53,6 +54,8 @@ import com.google.common.collect.Iterables;
|
||||
public class SimpleGroupStoreTest {
|
||||
|
||||
private SimpleGroupStore simpleGroupStore;
|
||||
private final ApplicationId appId =
|
||||
new DefaultApplicationId(2, "org.groupstore.test");
|
||||
|
||||
public static final DeviceId D1 = deviceId("of:1");
|
||||
|
||||
@ -167,16 +170,42 @@ public class SimpleGroupStoreTest {
|
||||
@Test
|
||||
public void testGroupStoreOperations() {
|
||||
// Set the Device AUDIT completed in the store
|
||||
simpleGroupStore.deviceInitialAuditCompleted(D1);
|
||||
simpleGroupStore.deviceInitialAuditCompleted(D1, true);
|
||||
|
||||
ApplicationId appId =
|
||||
new DefaultApplicationId(2, "org.groupstore.test");
|
||||
TestGroupKey key = new TestGroupKey("group1");
|
||||
// Testing storeGroup operation
|
||||
TestGroupKey newKey = new TestGroupKey("group1");
|
||||
testStoreAndGetGroup(newKey);
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
TestGroupKey currKey = newKey;
|
||||
testAddGroupEntryFromSB(currKey);
|
||||
|
||||
// Testing updateGroupDescription for ADD operation from northbound
|
||||
newKey = new TestGroupKey("group1AddBuckets");
|
||||
testAddBuckets(currKey, newKey);
|
||||
|
||||
// Testing updateGroupDescription for REMOVE operation from northbound
|
||||
currKey = newKey;
|
||||
newKey = new TestGroupKey("group1RemoveBuckets");
|
||||
testRemoveBuckets(currKey, newKey);
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
currKey = newKey;
|
||||
testUpdateGroupEntryFromSB(currKey);
|
||||
|
||||
// Testing deleteGroupDescription operation from northbound
|
||||
testDeleteGroup(currKey);
|
||||
|
||||
// Testing removeGroupEntry operation from southbound
|
||||
testRemoveGroupFromSB(currKey);
|
||||
}
|
||||
|
||||
// Testing storeGroup operation
|
||||
private void testStoreAndGetGroup(TestGroupKey key) {
|
||||
PortNumber[] ports = {PortNumber.portNumber(31),
|
||||
PortNumber.portNumber(32)};
|
||||
List<PortNumber> outPorts = new ArrayList<PortNumber>();
|
||||
outPorts.add(ports[0]);
|
||||
outPorts.add(ports[1]);
|
||||
outPorts.addAll(Arrays.asList(ports));
|
||||
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
for (PortNumber portNumber: outPorts) {
|
||||
@ -220,23 +249,44 @@ public class SimpleGroupStoreTest {
|
||||
}
|
||||
assertEquals(1, groupCount);
|
||||
simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
|
||||
}
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
private void testAddGroupEntryFromSB(TestGroupKey currKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
InternalGroupStoreDelegate addGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(key,
|
||||
groupBuckets,
|
||||
new InternalGroupStoreDelegate(currKey,
|
||||
existingGroup.buckets(),
|
||||
GroupEvent.Type.GROUP_ADDED);
|
||||
simpleGroupStore.setDelegate(addGroupEntryDelegate);
|
||||
simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
|
||||
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
||||
simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
|
||||
}
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
private void testUpdateGroupEntryFromSB(TestGroupKey currKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
|
||||
InternalGroupStoreDelegate updateGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(currKey,
|
||||
existingGroup.buckets(),
|
||||
GroupEvent.Type.GROUP_UPDATED);
|
||||
simpleGroupStore.setDelegate(updateGroupEntryDelegate);
|
||||
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
||||
simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
|
||||
}
|
||||
|
||||
// Testing updateGroupDescription for ADD operation from northbound
|
||||
private void testAddBuckets(TestGroupKey currKey, TestGroupKey addKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
buckets.addAll(existingGroup.buckets().buckets());
|
||||
|
||||
// Testing updateGroupDescription for ADD operation from northbound
|
||||
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
|
||||
PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
|
||||
PortNumber.portNumber(42)};
|
||||
List<PortNumber> newOutPorts = new ArrayList<PortNumber>();
|
||||
newOutPorts.add(newNeighborPorts[0]);
|
||||
newOutPorts.add(newNeighborPorts[1]);
|
||||
newOutPorts.addAll(Arrays.asList(newNeighborPorts[0]));
|
||||
|
||||
List<GroupBucket> toAddBuckets = new ArrayList<GroupBucket>();
|
||||
for (PortNumber portNumber: newOutPorts) {
|
||||
@ -258,79 +308,75 @@ public class SimpleGroupStoreTest {
|
||||
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
||||
simpleGroupStore.setDelegate(updateGroupDescDelegate);
|
||||
simpleGroupStore.updateGroupDescription(D1,
|
||||
key,
|
||||
currKey,
|
||||
UpdateType.ADD,
|
||||
toAddGroupBuckets,
|
||||
addKey);
|
||||
simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
|
||||
}
|
||||
|
||||
// Testing updateGroupDescription for REMOVE operation from northbound
|
||||
private void testRemoveBuckets(TestGroupKey currKey, TestGroupKey removeKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
|
||||
buckets.addAll(existingGroup.buckets().buckets());
|
||||
|
||||
// Testing updateGroupDescription for REMOVE operation from northbound
|
||||
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
|
||||
List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
|
||||
toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
|
||||
toRemoveBuckets.add(updatedGroupBuckets.buckets().get(1));
|
||||
|
||||
// There should be 4 buckets in the current group
|
||||
toRemoveBuckets.add(buckets.remove(0));
|
||||
toRemoveBuckets.add(buckets.remove(1));
|
||||
GroupBuckets toRemoveGroupBuckets = new GroupBuckets(toRemoveBuckets);
|
||||
List<GroupBucket> remainingBuckets = new ArrayList<GroupBucket>();
|
||||
remainingBuckets.add(updatedGroupBuckets.buckets().get(2));
|
||||
remainingBuckets.add(updatedGroupBuckets.buckets().get(3));
|
||||
GroupBuckets remainingGroupBuckets = new GroupBuckets(remainingBuckets);
|
||||
|
||||
GroupBuckets remainingGroupBuckets = new GroupBuckets(buckets);
|
||||
InternalGroupStoreDelegate removeGroupDescDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
|
||||
simpleGroupStore.setDelegate(removeGroupDescDelegate);
|
||||
simpleGroupStore.updateGroupDescription(D1,
|
||||
addKey,
|
||||
currKey,
|
||||
UpdateType.REMOVE,
|
||||
toRemoveGroupBuckets,
|
||||
removeKey);
|
||||
simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
|
||||
}
|
||||
|
||||
// Testing getGroup operation
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
||||
checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
|
||||
|
||||
// Testing addOrUpdateGroupEntry operation from southbound
|
||||
InternalGroupStoreDelegate updateGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
GroupEvent.Type.GROUP_UPDATED);
|
||||
simpleGroupStore.setDelegate(updateGroupEntryDelegate);
|
||||
simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
|
||||
simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
|
||||
|
||||
// Testing deleteGroupDescription operation from northbound
|
||||
// Testing deleteGroupDescription operation from northbound
|
||||
private void testDeleteGroup(TestGroupKey currKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
InternalGroupStoreDelegate deleteGroupDescDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
new InternalGroupStoreDelegate(currKey,
|
||||
existingGroup.buckets(),
|
||||
GroupEvent.Type.GROUP_REMOVE_REQUESTED);
|
||||
simpleGroupStore.setDelegate(deleteGroupDescDelegate);
|
||||
simpleGroupStore.deleteGroupDescription(D1, removeKey);
|
||||
simpleGroupStore.deleteGroupDescription(D1, currKey);
|
||||
simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
|
||||
}
|
||||
|
||||
// Testing removeGroupEntry operation from southbound
|
||||
// Testing removeGroupEntry operation from southbound
|
||||
private void testRemoveGroupFromSB(TestGroupKey currKey) {
|
||||
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
InternalGroupStoreDelegate removeGroupEntryDelegate =
|
||||
new InternalGroupStoreDelegate(removeKey,
|
||||
remainingGroupBuckets,
|
||||
new InternalGroupStoreDelegate(currKey,
|
||||
existingGroup.buckets(),
|
||||
GroupEvent.Type.GROUP_REMOVED);
|
||||
simpleGroupStore.setDelegate(removeGroupEntryDelegate);
|
||||
simpleGroupStore.removeGroupEntry(existingGroup);
|
||||
|
||||
// Testing getGroup operation
|
||||
existingGroup = simpleGroupStore.getGroup(D1, removeKey);
|
||||
existingGroup = simpleGroupStore.getGroup(D1, currKey);
|
||||
assertEquals(null, existingGroup);
|
||||
assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1)));
|
||||
assertEquals(0, simpleGroupStore.getGroupCount(D1));
|
||||
|
||||
simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupOperationFailure() {
|
||||
|
||||
simpleGroupStore.deviceInitialAuditCompleted(D1);
|
||||
simpleGroupStore.deviceInitialAuditCompleted(D1, true);
|
||||
|
||||
ApplicationId appId =
|
||||
new DefaultApplicationId(2, "org.groupstore.test");
|
||||
@ -408,8 +454,6 @@ public class SimpleGroupStoreTest {
|
||||
GroupEvent.Type.GROUP_REMOVE_FAILED);
|
||||
simpleGroupStore.setDelegate(checkGroupDelFailureDelegate);
|
||||
simpleGroupStore.groupOperationFailed(D1, groupDelOp);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user