Srikanth Vavilapalli
Committed by Gerrit Code Review

ONOS-985: Sample integration test application for group subsystem

Change-Id: I68352f922e5c7a0800fcc4fa839955769bf925a6
Showing 20 changed files with 2106 additions and 76 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 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);
}
}
}
/*
* 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 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();
}
}
}
\ 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.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;
}
}
}
......
......@@ -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
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
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(),
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,9 +453,13 @@ public class SimpleGroupStore
}
@Override
public void deviceInitialAuditCompleted(DeviceId deviceId) {
public void deviceInitialAuditCompleted(DeviceId deviceId,
boolean completed) {
synchronized (deviceAuditStatus) {
deviceAuditStatus.putIfAbsent(deviceId, true);
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);
......@@ -464,13 +473,21 @@ public class SimpleGroupStore
storeGroupDescriptionInternal(tmp);
}
getPendingGroupKeyTable(deviceId).clear();
} else {
if (deviceAuditStatus.get(deviceId)) {
log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
+ "status for device {}", deviceId);
deviceAuditStatus.put(deviceId, false);
}
}
}
}
@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);
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
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
private void testAddBuckets(TestGroupKey currKey, TestGroupKey addKey) {
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
buckets.addAll(existingGroup.buckets().buckets());
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
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
private void testRemoveBuckets(TestGroupKey currKey, TestGroupKey removeKey) {
Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
List<GroupBucket> buckets = new ArrayList<GroupBucket>();
buckets.addAll(existingGroup.buckets().buckets());
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
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
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);
}
}
......