Srikanth Vavilapalli
Committed by Gerrit Code Review

ONOS-985: Sample integration test application for group subsystem

Change-Id: I68352f922e5c7a0800fcc4fa839955769bf925a6
Showing 20 changed files with 1203 additions and 19 deletions
<?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>
/*
* 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;
}
}
/*
* 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;
}
}
}
}
\ No newline at end of file
/*
* 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;
}
}
/*
* 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();
}
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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();
}
}
\ No newline at end of file
......
......@@ -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();
}
}
\ No newline at end of file
......
......@@ -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,17 +70,22 @@ 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;
@Activate
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;
}
}
}
......
......@@ -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;
}
}
......