ONOS-985: Sample integration test application for group subsystem
Change-Id: I68352f922e5c7a0800fcc4fa839955769bf925a6
diff --git a/apps/grouphandler/pom.xml b/apps/grouphandler/pom.xml
new file mode 100644
index 0000000..a245b9f
--- /dev/null
+++ b/apps/grouphandler/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-grouphandler</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS sample application using group service</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-nio</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-netty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-misc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultEdgeGroupHandler.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultEdgeGroupHandler.java
new file mode 100644
index 0000000..d5d489c
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultEdgeGroupHandler.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+
+/**
+ * Default ECMP group handler creation module for an edge device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume device D0 is connected to 2 neighbors (D1 and D2 ).
+ * The following groups will be created in D0:
+ * 1) all ports to D1 + with no label push,
+ * 2) all ports to D1 + with label 102 pushed,
+ * 3) all ports to D1 + with label 103 pushed,
+ * 4) all ports to D2 + with no label push,
+ * 5) all ports to D2 + with label 101 pushed,
+ * 6) all ports to D2 + with label 103 pushed,
+ * 7) all ports to D1 and D2 + with label 103 pushed
+ */
+public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
+
+ protected DefaultEdgeGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ GroupService groupService) {
+ super(deviceId, appId, config, linkService, groupService);
+ }
+
+ @Override
+ public void createGroups() {
+ log.debug("Creating default groups "
+ + "for edge device {}", deviceId);
+ Set<DeviceId> neighbors = devicePortMap.keySet();
+ if (neighbors == null || neighbors.isEmpty()) {
+ return;
+ }
+
+ // Create all possible Neighbor sets from this router
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(neighbors);
+ log.trace("createGroupsAtEdgeRouter: The size of neighbor powerset "
+ + "for sw {} is {}", deviceId, powerSet.size());
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ List<Integer> groupSegmentIds =
+ getSegmentIdsTobePairedWithNeighborSet(combo);
+ for (Integer sId : groupSegmentIds) {
+ NeighborSet ns = new NeighborSet(combo, sId);
+ log.trace("createGroupsAtEdgeRouter: sw {} "
+ + "combo {} sId {} ns {}",
+ deviceId, combo, sId, ns);
+ nsSet.add(ns);
+ }
+ }
+ log.trace("createGroupsAtEdgeRouter: The neighborset "
+ + "with label for sw {} is {}",
+ deviceId, nsSet);
+
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newNeighbor(Link newNeighborLink) {
+ log.debug("New Neighbor: Updating groups "
+ + "for edge device {}", deviceId);
+ // Recompute neighbor power set
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ // Compute new neighbor sets due to the addition of new neighbor
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newPortToExistingNeighbor(Link newNeighborLink) {
+ log.debug("New port to existing neighbor: Updating "
+ + "groups for edge device {}", deviceId);
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ for (NeighborSet ns : nsSet) {
+ // Create the new bucket to be updated
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(newNeighborLink.src().port())
+ .setEthDst(deviceConfig.getDeviceMac(
+ newNeighborLink.dst().deviceId()))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(ns.getEdgeLabel());
+ GroupBucket updatedBucket = DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build());
+ GroupBuckets updatedBuckets = new GroupBuckets(
+ Arrays.asList(updatedBucket));
+ log.debug("newPortToExistingNeighborAtEdgeRouter: "
+ + "groupService.addBucketsToGroup for neighborset{}", ns);
+ groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
+ }
+ }
+
+ @Override
+ protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+ DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+ Set<DeviceId> tmp = new HashSet<DeviceId>();
+ tmp.addAll(updatedNeighbors);
+ tmp.remove(impactedNeighbor);
+ Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+ // Compute the impacted neighbor sets
+ powerSet.removeAll(tmpPowerSet);
+
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ List<Integer> groupSegmentIds =
+ getSegmentIdsTobePairedWithNeighborSet(combo);
+ for (Integer sId : groupSegmentIds) {
+ NeighborSet ns = new NeighborSet(combo, sId);
+ log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
+ + "combo {} sId {} ns {}",
+ deviceId, combo, sId, ns);
+ nsSet.add(ns);
+ }
+ }
+ log.trace("computeImpactedNeighborsetForPortEvent: The neighborset "
+ + "with label for sw {} is {}",
+ deviceId, nsSet);
+ return nsSet;
+ }
+
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandler.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandler.java
new file mode 100644
index 0000000..1421f85
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandler.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+
+/**
+ * Default ECMP group handler creation module. This
+ * component creates a set of ECMP groups for every neighbor
+ * that this device is connected to based on whether the
+ * current device is an edge device or a transit device.
+ */
+public class DefaultGroupHandler {
+ protected final Logger log = getLogger(getClass());
+
+ protected final DeviceId deviceId;
+ protected final ApplicationId appId;
+ protected final DeviceProperties deviceConfig;
+ protected final List<Integer> allSegmentIds;
+ protected final int nodeSegmentId;
+ protected final boolean isEdgeRouter;
+ protected final MacAddress nodeMacAddr;
+ protected LinkService linkService;
+ protected GroupService groupService;
+
+ protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
+ new HashMap<DeviceId, Set<PortNumber>>();
+ protected HashMap<PortNumber, DeviceId> portDeviceMap =
+ new HashMap<PortNumber, DeviceId>();
+
+ private GroupListener listener = new InternalGroupListener();
+
+ protected DefaultGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ GroupService groupService) {
+ this.deviceId = checkNotNull(deviceId);
+ this.appId = checkNotNull(appId);
+ this.deviceConfig = checkNotNull(config);
+ this.linkService = checkNotNull(linkService);
+ this.groupService = checkNotNull(groupService);
+ allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
+ nodeSegmentId = config.getSegmentId(deviceId);
+ isEdgeRouter = config.isEdgeDevice(deviceId);
+ nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
+
+ this.groupService.addListener(listener);
+
+ populateNeighborMaps();
+ }
+
+ /**
+ * Creates a group handler object based on the type of device. If
+ * device is of edge type it returns edge group handler, else it
+ * returns transit group handler.
+ *
+ * @param deviceId device identifier
+ * @param appId application identifier
+ * @param config interface to retrieve the device properties
+ * @param linkService link service object
+ * @param groupService group service object
+ * @return default group handler type
+ */
+ public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ GroupService groupService) {
+ if (config.isEdgeDevice(deviceId)) {
+ return new DefaultEdgeGroupHandler(deviceId,
+ appId,
+ config,
+ linkService,
+ groupService);
+ } else {
+ return new DefaultTransitGroupHandler(deviceId,
+ appId,
+ config,
+ linkService,
+ groupService);
+ }
+ }
+
+ /**
+ * Creates the auto created groups for this device based on the
+ * current snapshot of the topology.
+ */
+ //Empty implementations to be overridden by derived classes
+ public void createGroups() {
+ }
+
+ /**
+ * Performs group creation or update procedures when a new link
+ * is discovered on this device.
+ *
+ * @param newLink new neighbor link
+ */
+ public void linkUp(Link newLink) {
+ if (newLink.type() != Link.Type.DIRECT) {
+ log.warn("linkUp: unknown link type");
+ return;
+ }
+
+ if (!newLink.src().deviceId().equals(deviceId)) {
+ log.warn("linkUp: deviceId{} doesn't match with link src{}",
+ deviceId,
+ newLink.src().deviceId());
+ return;
+ }
+
+ log.debug("Device {} linkUp at local port {} to neighbor {}",
+ deviceId, newLink.src().port(), newLink.dst().deviceId());
+ if (devicePortMap.get(newLink.dst().deviceId()) == null) {
+ // New Neighbor
+ newNeighbor(newLink);
+ } else {
+ // Old Neighbor
+ newPortToExistingNeighbor(newLink);
+ }
+ }
+
+ /**
+ * Performs group recovery procedures when a port goes down
+ * on this device.
+ *
+ * @param port port number that has gone down
+ */
+ public void portDown(PortNumber port) {
+ if (portDeviceMap.get(port) == null) {
+ log.warn("portDown: unknown port");
+ return;
+ }
+ log.debug("Device {} portDown {} to neighbor {}",
+ deviceId, port, portDeviceMap.get(port));
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ portDeviceMap.get(port),
+ devicePortMap.keySet());
+ for (NeighborSet ns : nsSet) {
+ // Create the bucket to be removed
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(port)
+ .setEthDst(deviceConfig.getDeviceMac(
+ portDeviceMap.get(port)))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(ns.getEdgeLabel());
+ GroupBucket removeBucket = DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build());
+ GroupBuckets removeBuckets = new GroupBuckets(
+ Arrays.asList(removeBucket));
+ log.debug("portDown in device{}: "
+ + "groupService.removeBucketsFromGroup "
+ + "for neighborset{}", deviceId, ns);
+ groupService.removeBucketsFromGroup(deviceId,
+ ns,
+ removeBuckets,
+ ns,
+ appId);
+ }
+
+ devicePortMap.get(portDeviceMap.get(port)).remove(port);
+ portDeviceMap.remove(port);
+ }
+
+ /**
+ * Returns a group associated with the key.
+ *
+ * @param key cookie associated with the group
+ * @return group if found or null
+ */
+ public Group getGroup(GroupKey key) {
+ return groupService.getGroup(deviceId, key);
+ }
+
+ //Empty implementation
+ protected void newNeighbor(Link newLink) {
+ }
+
+ //Empty implementation
+ protected void newPortToExistingNeighbor(Link newLink) {
+ }
+
+ //Empty implementation
+ protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+ DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ return null;
+ }
+
+ private void populateNeighborMaps() {
+ Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
+ for (Link link:outgoingLinks) {
+ if (link.type() != Link.Type.DIRECT) {
+ continue;
+ }
+ addNeighborAtPort(link.dst().deviceId(), link.src().port());
+ }
+ }
+
+ protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
+ // Update DeviceToPort database
+ log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
+ deviceId, neighborId, portToNeighbor);
+ if (devicePortMap.get(neighborId) != null) {
+ devicePortMap.get(neighborId).add(portToNeighbor);
+ } else {
+ Set<PortNumber> ports = new HashSet<PortNumber>();
+ ports.add(portToNeighbor);
+ devicePortMap.put(neighborId, ports);
+ }
+
+ // Update portToDevice database
+ if (portDeviceMap.get(portToNeighbor) == null) {
+ portDeviceMap.put(portToNeighbor, neighborId);
+ }
+ }
+
+ protected Set<Set<DeviceId>>
+ getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
+ List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
+ Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
+ // get the number of elements in the neighbors
+ int elements = list.size();
+ // the number of members of a power set is 2^n
+ // including the empty set
+ int powerElements = (1 << elements);
+
+ // run a binary counter for the number of power elements
+ // NOTE: Exclude empty set
+ for (long i = 1; i < powerElements; i++) {
+ Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
+ for (int j = 0; j < elements; j++) {
+ if ((i >> j) % 2 == 1) {
+ neighborSubSet.add(list.get(j));
+ }
+ }
+ sets.add(neighborSubSet);
+ }
+ return sets;
+ }
+
+ private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
+ return (deviceConfig.getSegmentId(deviceId) == sId);
+ }
+
+ protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
+ Set<DeviceId> neighbors) {
+
+ List<Integer> nsSegmentIds = new ArrayList<Integer>();
+
+ // Add one entry for "no label" (-1) to the list if
+ // dpid list has not more than one node/neighbor as
+ // there will never be a case a packet going to more than one
+ // neighbor without a label at an edge router
+ if (neighbors.size() == 1) {
+ nsSegmentIds.add(-1);
+ }
+ // Filter out SegmentIds matching with the
+ // nodes in the combo
+ for (Integer sId : allSegmentIds) {
+ if (sId.equals(nodeSegmentId)) {
+ continue;
+ }
+ boolean filterOut = false;
+ // Check if the edge label being set is of
+ // any node in the Neighbor set
+ for (DeviceId deviceId : neighbors) {
+ if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
+ filterOut = true;
+ break;
+ }
+ }
+ if (!filterOut) {
+ nsSegmentIds.add(sId);
+ }
+ }
+ return nsSegmentIds;
+ }
+
+ protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
+ for (NeighborSet ns : nsSet) {
+ // Create the bucket array from the neighbor set
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ for (DeviceId d : ns.getDeviceIds()) {
+ for (PortNumber sp : devicePortMap.get(d)) {
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(sp)
+ .setEthDst(deviceConfig.getDeviceMac(d))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(ns.getEdgeLabel());
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ }
+ GroupBuckets groupBuckets = new GroupBuckets(buckets);
+ GroupDescription newGroupDesc = new DefaultGroupDescription(
+ deviceId,
+ Group.Type.SELECT,
+ groupBuckets,
+ ns,
+ appId);
+ log.debug("createGroupsFromNeighborsets: "
+ + "groupService.addGroup for neighborset{}", ns);
+ groupService.addGroup(newGroupDesc);
+ }
+ }
+
+ protected void handleGroupEvent(GroupEvent event) {
+ switch (event.type()) {
+ case GROUP_ADDED:
+ log.debug("Received GROUP_ADDED from group service "
+ + "for device {} with group key{} with id{}",
+ event.subject().deviceId(),
+ event.subject().appCookie(),
+ event.subject().id());
+ break;
+ case GROUP_UPDATED:
+ log.trace("Received GROUP_UPDATED from group service "
+ + "for device {} with group key{} with id{}",
+ event.subject().deviceId(),
+ event.subject().appCookie(),
+ event.subject().id());
+ break;
+ case GROUP_REMOVED:
+ log.debug("Received GROUP_REMOVED from group service "
+ + "for device {} with group key{} with id{}",
+ event.subject().deviceId(),
+ event.subject().appCookie(),
+ event.subject().id());
+ break;
+ default:
+ break;
+ }
+ }
+
+ private class InternalGroupListener implements GroupListener {
+
+ @Override
+ public void event(GroupEvent event) {
+ handleGroupEvent(event);
+ }
+ }
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandlerApp.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandlerApp.java
new file mode 100644
index 0000000..2bd2b4e
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultGroupHandlerApp.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+/**
+ * Sample application to verify group subsystem end to end.
+ * This application expects a network of maximum of six connected
+ * devices for the test to work. For every device in the network,
+ * this test application launches a default group handler function
+ * that creates ECMP groups for every neighbor the device is
+ * connected to.
+ */
+@Component(immediate = true)
+public class DefaultGroupHandlerApp {
+
+ private final Logger log = getLogger(getClass());
+
+ private final DeviceProperties config = new DeviceConfiguration();
+ private ApplicationId appId;
+ private HashMap<DeviceId, DefaultGroupHandler> dghMap =
+ new HashMap<DeviceId, DefaultGroupHandler>();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private DeviceListener deviceListener = new InternalDeviceListener();
+ private LinkListener linkListener = new InternalLinkListener();
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.defaultgrouphandler");
+ log.info("DefaultGroupHandlerApp Activating");
+ deviceService.addListener(deviceListener);
+ linkService.addListener(linkListener);
+ for (Device device: deviceService.getDevices()) {
+ log.debug("Initiating default group handling for {}", device.id());
+ DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(device.id(),
+ appId,
+ config,
+ linkService,
+ groupService);
+ dgh.createGroups();
+ dghMap.put(device.id(), dgh);
+ }
+ log.info("Activated");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ dghMap.clear();
+ }
+
+ public class DeviceConfiguration implements DeviceProperties {
+ private final List<Integer> allSegmentIds =
+ Arrays.asList(101, 102, 103, 104, 105, 106);
+ private HashMap<DeviceId, Integer> deviceSegmentIdMap =
+ new HashMap<DeviceId, Integer>() {
+ {
+ put(DeviceId.deviceId("of:0000000000000001"), 101);
+ put(DeviceId.deviceId("of:0000000000000002"), 102);
+ put(DeviceId.deviceId("of:0000000000000003"), 103);
+ put(DeviceId.deviceId("of:0000000000000004"), 104);
+ put(DeviceId.deviceId("of:0000000000000005"), 105);
+ put(DeviceId.deviceId("of:0000000000000006"), 106);
+ }
+ };
+ private final HashMap<DeviceId, MacAddress> deviceMacMap =
+ new HashMap<DeviceId, MacAddress>() {
+ {
+ put(DeviceId.deviceId("of:0000000000000001"),
+ MacAddress.valueOf("00:00:00:00:00:01"));
+ put(DeviceId.deviceId("of:0000000000000002"),
+ MacAddress.valueOf("00:00:00:00:00:02"));
+ put(DeviceId.deviceId("of:0000000000000003"),
+ MacAddress.valueOf("00:00:00:00:00:03"));
+ put(DeviceId.deviceId("of:0000000000000004"),
+ MacAddress.valueOf("00:00:00:00:00:04"));
+ put(DeviceId.deviceId("of:0000000000000005"),
+ MacAddress.valueOf("00:00:00:00:00:05"));
+ put(DeviceId.deviceId("of:0000000000000006"),
+ MacAddress.valueOf("00:00:00:00:00:06"));
+ }
+ };
+
+ @Override
+ public int getSegmentId(DeviceId deviceId) {
+ if (deviceSegmentIdMap.get(deviceId) != null) {
+ log.debug("getSegmentId for device{} is {}",
+ deviceId,
+ deviceSegmentIdMap.get(deviceId));
+ return deviceSegmentIdMap.get(deviceId);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ @Override
+ public MacAddress getDeviceMac(DeviceId deviceId) {
+ if (deviceMacMap.get(deviceId) != null) {
+ log.debug("getDeviceMac for device{} is {}",
+ deviceId,
+ deviceMacMap.get(deviceId));
+ return deviceMacMap.get(deviceId);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ @Override
+ public boolean isEdgeDevice(DeviceId deviceId) {
+ return true;
+ }
+ @Override
+ public List<Integer> getAllDeviceSegmentIds() {
+ return allSegmentIds;
+ }
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ log.debug("Initiating default group handling for {}", event.subject().id());
+ DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(
+ event.subject().id(),
+ appId,
+ config,
+ linkService,
+ groupService);
+ dgh.createGroups();
+ dghMap.put(event.subject().id(), dgh);
+ break;
+ case PORT_REMOVED:
+ if (dghMap.get(event.subject().id()) != null) {
+ dghMap.get(event.subject().id()).portDown(event.port().number());
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+
+ private class InternalLinkListener implements LinkListener {
+
+ @Override
+ public void event(LinkEvent event) {
+ switch (event.type()) {
+ case LINK_ADDED:
+ if (dghMap.get(event.subject().src().deviceId()) != null) {
+ dghMap.get(event.subject().src().deviceId()).linkUp(event.subject());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultTransitGroupHandler.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultTransitGroupHandler.java
new file mode 100644
index 0000000..9704539
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultTransitGroupHandler.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+
+/**
+ * Default ECMP group handler creation module for a transit device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume transit device D1 is connected to 2 neighbors (D0 and D3 ).
+ * The following groups will be created in D1:
+ * 1) all ports to D0 + with no label push,
+ * 2) all ports to D3 + with no label push,
+ */
+public class DefaultTransitGroupHandler extends DefaultGroupHandler {
+
+ protected DefaultTransitGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ GroupService groupService) {
+ super(deviceId, appId, config, linkService, groupService);
+ }
+
+ @Override
+ public void createGroups() {
+ Set<DeviceId> neighbors = devicePortMap.keySet();
+ if (neighbors == null || neighbors.isEmpty()) {
+ return;
+ }
+
+ // Create all possible Neighbor sets from this router
+ // NOTE: Avoid any pairings of edge routers only
+ Set<Set<DeviceId>> sets = getPowerSetOfNeighbors(neighbors);
+ sets = filterEdgeRouterOnlyPairings(sets);
+ log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
+ + "for sw {} is {}", deviceId, sets.size());
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : sets) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ NeighborSet ns = new NeighborSet(combo);
+ log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+ deviceId, combo, ns);
+ nsSet.add(ns);
+ }
+ log.debug("createGroupsAtTransitRouter: The neighborset with label "
+ + "for sw {} is {}", deviceId, nsSet);
+
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newNeighbor(Link newNeighborLink) {
+ log.debug("New Neighbor: Updating groups for "
+ + "transit device {}", deviceId);
+ // Recompute neighbor power set
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ // Compute new neighbor sets due to the addition of new neighbor
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newPortToExistingNeighbor(Link newNeighborLink) {
+ log.debug("New port to existing neighbor: Updating "
+ + "groups for transit device {}", deviceId);
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ for (NeighborSet ns : nsSet) {
+ // Create the new bucket to be updated
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(newNeighborLink.src().port())
+ .setEthDst(deviceConfig.getDeviceMac(
+ newNeighborLink.dst().deviceId()))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(ns.getEdgeLabel());
+ GroupBucket updatedBucket = DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build());
+ GroupBuckets updatedBuckets = new GroupBuckets(
+ Arrays.asList(updatedBucket));
+ log.debug("newPortToExistingNeighborAtEdgeRouter: "
+ + "groupService.addBucketsToGroup for neighborset{}", ns);
+ groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
+ }
+ }
+
+ @Override
+ protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+ DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+ Set<DeviceId> tmp = updatedNeighbors;
+ tmp.remove(impactedNeighbor);
+ Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+ // Compute the impacted neighbor sets
+ powerSet.removeAll(tmpPowerSet);
+
+ powerSet = filterEdgeRouterOnlyPairings(powerSet);
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ NeighborSet ns = new NeighborSet(combo);
+ log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+ deviceId, combo, ns);
+ nsSet.add(ns);
+ }
+ log.debug("computeImpactedNeighborsetForPortEvent: The neighborset with label "
+ + "for sw {} is {}", deviceId, nsSet);
+
+ return nsSet;
+ }
+
+ private Set<Set<DeviceId>> filterEdgeRouterOnlyPairings(Set<Set<DeviceId>> sets) {
+ Set<Set<DeviceId>> fiteredSets = new HashSet<Set<DeviceId>>();
+ for (Set<DeviceId> deviceSubSet : sets) {
+ if (deviceSubSet.size() > 1) {
+ boolean avoidEdgeRouterPairing = true;
+ for (DeviceId device : deviceSubSet) {
+ if (!deviceConfig.isEdgeDevice(device)) {
+ avoidEdgeRouterPairing = false;
+ break;
+ }
+ }
+ if (!avoidEdgeRouterPairing) {
+ fiteredSets.add(deviceSubSet);
+ }
+ } else {
+ fiteredSets.add(deviceSubSet);
+ }
+ }
+ return fiteredSets;
+ }
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DeviceProperties.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DeviceProperties.java
new file mode 100644
index 0000000..685fa78
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/DeviceProperties.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import java.util.List;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Mechanism through which group handler module retrieves
+ * the device specific attributes such as segment ID,
+ * Mac address...etc from group handler applications.
+ */
+public interface DeviceProperties {
+ /**
+ * Returns the segment id of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @return segment id of a device
+ */
+ int getSegmentId(DeviceId deviceId);
+ /**
+ * Returns the Mac address of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @return mac address of a device
+ */
+ MacAddress getDeviceMac(DeviceId deviceId);
+ /**
+ * Indicates whether a device is edge device or transit/core device.
+ *
+ * @param deviceId device identifier
+ * @return boolean
+ */
+ boolean isEdgeDevice(DeviceId deviceId);
+ /**
+ * Returns all segment IDs to be considered in building auto
+ *
+ * created groups.
+ * @return list of segment IDs
+ */
+ List<Integer> getAllDeviceSegmentIds();
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/GroupBucketIdentifier.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/GroupBucketIdentifier.java
new file mode 100644
index 0000000..cf0ae5e
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/GroupBucketIdentifier.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.group.GroupKey;
+
+/**
+ * Representation of policy group bucket identifier. Not exposed to
+ * the application and only to be used internally.
+ */
+public class GroupBucketIdentifier {
+ private int label;
+ private BucketOutputType type;
+ private PortNumber outPort;
+ private GroupKey outGroup;
+
+ protected enum BucketOutputType {
+ PORT,
+ GROUP
+ }
+
+ protected GroupBucketIdentifier(int label,
+ PortNumber outPort) {
+ this.label = label;
+ this.type = BucketOutputType.PORT;
+ this.outPort = checkNotNull(outPort);
+ this.outGroup = null;
+ }
+
+ protected GroupBucketIdentifier(int label,
+ GroupKey outGroup) {
+ this.label = label;
+ this.type = BucketOutputType.GROUP;
+ this.outPort = null;
+ this.outGroup = checkNotNull(outGroup);
+ }
+
+ protected int label() {
+ return this.label;
+ }
+
+ protected BucketOutputType type() {
+ return this.type;
+ }
+
+ protected PortNumber outPort() {
+ return this.outPort;
+ }
+
+ protected GroupKey outGroup() {
+ return this.outGroup;
+ }
+}
+
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/NeighborSet.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/NeighborSet.java
new file mode 100644
index 0000000..497b352
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/NeighborSet.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.GroupKey;
+
+/**
+ * Representation of a set of neighbor switch dpids along with edge node
+ * label. Meant to be used as a lookup-key in a hash-map to retrieve an
+ * ECMP-group that hashes packets to a set of ports connecting to the
+ * neighbors in this set.
+ */
+public class NeighborSet implements GroupKey {
+ private final Set<DeviceId> neighbors;
+ private final int edgeLabel;
+
+ /**
+ * Constructor with set of neighbors. Edge label is
+ * default to -1.
+ *
+ * @param neighbors set of neighbors to be part of neighbor set
+ */
+ public NeighborSet(Set<DeviceId> neighbors) {
+ checkNotNull(neighbors);
+ this.edgeLabel = -1;
+ this.neighbors = new HashSet<DeviceId>();
+ this.neighbors.addAll(neighbors);
+ }
+
+ /**
+ * Constructor with set of neighbors and edge label.
+ *
+ * @param neighbors set of neighbors to be part of neighbor set
+ * @param edgeLabel label to be pushed as part of group operation
+ */
+ public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
+ checkNotNull(neighbors);
+ this.edgeLabel = edgeLabel;
+ this.neighbors = new HashSet<DeviceId>();
+ this.neighbors.addAll(neighbors);
+ }
+
+ /**
+ * Default constructor for kryo serialization.
+ */
+ public NeighborSet() {
+ this.edgeLabel = -1;
+ this.neighbors = new HashSet<DeviceId>();
+ }
+
+ /**
+ * Gets the neighbors part of neighbor set.
+ *
+ * @return set of neighbor identifiers
+ */
+ public Set<DeviceId> getDeviceIds() {
+ return neighbors;
+ }
+
+ /**
+ * Gets the label associated with neighbor set.
+ *
+ * @return integer
+ */
+ public int getEdgeLabel() {
+ return edgeLabel;
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof NeighborSet)) {
+ return false;
+ }
+ NeighborSet that = (NeighborSet) o;
+ return (this.neighbors.containsAll(that.neighbors) &&
+ that.neighbors.containsAll(this.neighbors) &&
+ (this.edgeLabel == that.edgeLabel));
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (DeviceId d : neighbors) {
+ combinedHash = combinedHash + Objects.hash(d);
+ }
+ result = 31 * result + combinedHash + Objects.hash(edgeLabel);
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return " Neighborset Sw: " + neighbors
+ + " and Label: " + edgeLabel;
+ }
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupHandler.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupHandler.java
new file mode 100644
index 0000000..4f57bd3
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupHandler.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.grouphandler.GroupBucketIdentifier.BucketOutputType;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+
+/**
+ * A module to create group chains based on the specified device
+ * ports and label stack to be applied on each port.
+ */
+public class PolicyGroupHandler extends DefaultGroupHandler {
+
+ private final Logger log = getLogger(getClass());
+ private HashMap<GroupKey, GroupKey> dependentGroups =
+ new HashMap<GroupKey, GroupKey>();
+
+ /**
+ * Creates policy group handler object.
+ *
+ * @param deviceId device identifier
+ * @param appId application identifier
+ * @param config interface to retrieve the device properties
+ * @param linkService link service object
+ * @param groupService group service object
+ * @return policy group handler type
+ */
+ public PolicyGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ GroupService groupService) {
+ super(deviceId, appId, config, linkService, groupService);
+ }
+
+ public PolicyGroupIdentifier createPolicyGroupChain(String id,
+ List<PolicyGroupParams> params) {
+ List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
+ for (PolicyGroupParams param: params) {
+ List<PortNumber> ports = param.getPorts();
+ if (ports == null) {
+ log.warn("createPolicyGroupChain in sw {} with wrong "
+ + "input parameters", deviceId);
+ return null;
+ }
+
+ int labelStackSize = (param.getLabelStack() != null) ?
+ param.getLabelStack().size() : 0;
+
+ if (labelStackSize > 1) {
+ for (PortNumber sp : ports) {
+ PolicyGroupIdentifier previousGroupkey = null;
+ DeviceId neighbor = portDeviceMap.get(sp);
+ for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+ int label = param.getLabelStack().get(idx).intValue();
+ if (idx == (labelStackSize - 1)) {
+ // Innermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ bucketIds.add(bucketId);
+ } else if (idx == 0) {
+ // Outermost Group
+ List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Arrays.asList(param),
+ Arrays.asList(bucketId));
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(sp)
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(label);
+ outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));
+ GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(outBuckets));
+ //TODO: BoS
+ previousGroupkey = key;
+ groupService.addGroup(desc);
+ } else {
+ // Intermediate Groups
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Arrays.asList(param),
+ Arrays.asList(bucketId));
+ // Add to group dependency list
+ dependentGroups.put(previousGroupkey, key);
+ previousGroupkey = key;
+ }
+ }
+ }
+ } else {
+ int label = -1;
+ if (labelStackSize == 1) {
+ label = param.getLabelStack().get(0).intValue();
+ }
+ for (PortNumber sp : ports) {
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ bucketIds.add(bucketId);
+ }
+ }
+ }
+ PolicyGroupIdentifier innermostGroupkey = null;
+ if (!bucketIds.isEmpty()) {
+ innermostGroupkey = new
+ PolicyGroupIdentifier(id,
+ params,
+ bucketIds);
+ // Add to group dependency list
+ boolean fullyResolved = true;
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ if (bucketId.type() == BucketOutputType.GROUP) {
+ dependentGroups.put(bucketId.outGroup(),
+ innermostGroupkey);
+ fullyResolved = false;
+ }
+ }
+
+ if (fullyResolved) {
+ List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ DeviceId neighbor = portDeviceMap.
+ get(bucketId.outPort());
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(bucketId.outPort())
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(bucketId.label());
+ //TODO: BoS
+ outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));
+ }
+ GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(outBuckets));
+ groupService.addGroup(desc);
+ }
+ }
+ return innermostGroupkey;
+ }
+
+ @Override
+ protected void handleGroupEvent(GroupEvent event) {
+ if (event.type() == GroupEvent.Type.GROUP_ADDED) {
+ if (dependentGroups.get(event.subject().appCookie()) != null) {
+ PolicyGroupIdentifier dependentGroupKey = (PolicyGroupIdentifier)
+ dependentGroups.get(event.subject().appCookie());
+ dependentGroups.remove(event.subject().appCookie());
+ boolean fullyResolved = true;
+ for (GroupBucketIdentifier bucketId:
+ dependentGroupKey.bucketIds()) {
+ if (bucketId.type() != BucketOutputType.GROUP) {
+ continue;
+ }
+ if (dependentGroups.containsKey(bucketId.outGroup())) {
+ fullyResolved = false;
+ break;
+ }
+ }
+
+ if (fullyResolved) {
+ List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+ for (GroupBucketIdentifier bucketId:
+ dependentGroupKey.bucketIds()) {
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.pushMpls()
+ .setMpls(bucketId.label());
+ //TODO: BoS
+ if (bucketId.type() == BucketOutputType.PORT) {
+ DeviceId neighbor = portDeviceMap.
+ get(bucketId.outPort());
+ tBuilder.setOutput(bucketId.outPort())
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr);
+ } else {
+ if (groupService.
+ getGroup(deviceId,
+ bucketId.outGroup()) == null) {
+ throw new IllegalStateException();
+ }
+ GroupId indirectGroupId = groupService.
+ getGroup(deviceId,
+ bucketId.outGroup()).id();
+ tBuilder.group(indirectGroupId);
+ }
+ outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));
+ }
+ GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(outBuckets));
+ groupService.addGroup(desc);
+ }
+ }
+ }
+ }
+
+ public GroupKey generatePolicyGroupKey(String id,
+ List<PolicyGroupParams> params) {
+ List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
+ for (PolicyGroupParams param: params) {
+ List<PortNumber> ports = param.getPorts();
+ if (ports == null) {
+ log.warn("generateGroupKey in sw {} with wrong "
+ + "input parameters", deviceId);
+ return null;
+ }
+
+ int labelStackSize = (param.getLabelStack() != null)
+ ? param.getLabelStack().size() : 0;
+
+ if (labelStackSize > 1) {
+ for (PortNumber sp : ports) {
+ PolicyGroupIdentifier previousGroupkey = null;
+ for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+ int label = param.getLabelStack().get(idx).intValue();
+ if (idx == (labelStackSize - 1)) {
+ // Innermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ bucketIds.add(bucketId);
+ } else if (idx == 0) {
+ // Outermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Arrays.asList(param),
+ Arrays.asList(bucketId));
+ previousGroupkey = key;
+ } else {
+ // Intermediate Groups
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Arrays.asList(param),
+ Arrays.asList(bucketId));
+ previousGroupkey = key;
+ }
+ }
+ }
+ } else {
+ int label = -1;
+ if (labelStackSize == 1) {
+ label = param.getLabelStack().get(0).intValue();
+ }
+ for (PortNumber sp : ports) {
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ bucketIds.add(bucketId);
+ }
+ }
+ }
+ PolicyGroupIdentifier innermostGroupkey = null;
+ if (!bucketIds.isEmpty()) {
+ innermostGroupkey = new
+ PolicyGroupIdentifier(id,
+ params,
+ bucketIds);
+ }
+ return innermostGroupkey;
+ }
+
+ public void removeGroupChain(GroupKey key) {
+ if (!(key instanceof PolicyGroupIdentifier)) {
+ throw new IllegalArgumentException();
+ }
+ List<GroupKey> groupsToBeDeleted = new ArrayList<GroupKey>();
+ groupsToBeDeleted.add(key);
+
+ Iterator<GroupKey> it = groupsToBeDeleted.iterator();
+
+ while (it.hasNext()) {
+ PolicyGroupIdentifier innerMostGroupKey =
+ (PolicyGroupIdentifier) it.next();
+ for (GroupBucketIdentifier bucketId:
+ innerMostGroupKey.bucketIds()) {
+ if (bucketId.type() != BucketOutputType.GROUP) {
+ groupsToBeDeleted.add(bucketId.outGroup());
+ }
+ }
+ groupService.removeGroup(deviceId, innerMostGroupKey, appId);
+ it.remove();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupIdentifier.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupIdentifier.java
new file mode 100644
index 0000000..d213e0c
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupIdentifier.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import java.util.List;
+
+import org.onosproject.net.group.GroupKey;
+
+/**
+ * Representation of policy based group identifiers.
+ * Opaque to group handler applications and only the outermost
+ * policy group identifier in a chain is visible to the applications.
+ */
+public class PolicyGroupIdentifier implements GroupKey {
+ private String id;
+ private List<PolicyGroupParams> inputParams;
+ private List<GroupBucketIdentifier> bucketIds;
+
+ /**
+ * Constructor.
+ *
+ * @param id unique identifier associated with the policy group
+ * @param input policy group params associated with this group
+ * @param bucketIds buckets associated with this group
+ */
+ protected PolicyGroupIdentifier(String id,
+ List<PolicyGroupParams> input,
+ List<GroupBucketIdentifier> bucketIds) {
+ this.id = id;
+ this.inputParams = input;
+ this.bucketIds = bucketIds;
+ }
+
+ /**
+ * Returns the bucket identifier list associated with the policy
+ * group identifier.
+ *
+ * @return list of bucket identifier
+ */
+ protected List<GroupBucketIdentifier> bucketIds() {
+ return this.bucketIds;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (PolicyGroupParams input:inputParams) {
+ combinedHash = combinedHash + input.hashCode();
+ }
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ combinedHash = combinedHash + bucketId.hashCode();
+ }
+ result = 31 * result + combinedHash;
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof PolicyGroupIdentifier) {
+ PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
+ boolean result = this.id.equals(that.id);
+ result = result &&
+ this.inputParams.containsAll(that.inputParams) &&
+ that.inputParams.containsAll(this.inputParams);
+ result = result &&
+ this.bucketIds.containsAll(that.bucketIds) &&
+ that.bucketIds.containsAll(this.bucketIds);
+ return result;
+ }
+
+ return false;
+ }
+}
diff --git a/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupParams.java b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupParams.java
new file mode 100644
index 0000000..d2fcffa
--- /dev/null
+++ b/apps/grouphandler/src/main/java/org/onosproject/grouphandler/PolicyGroupParams.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of parameters used to create policy based groups.
+ */
+public class PolicyGroupParams {
+ private final List<PortNumber> ports;
+ private final List<Integer> labelStack;
+
+ /**
+ * Constructor.
+ *
+ * @param labelStack mpls label stack to be applied on the ports
+ * @param ports ports to be part of the policy group
+ */
+ public PolicyGroupParams(List<Integer> labelStack,
+ List<PortNumber> ports) {
+ this.ports = checkNotNull(ports);
+ this.labelStack = checkNotNull(labelStack);
+ }
+
+ /**
+ * Returns the ports associated with the policy group params.
+ *
+ * @return list of port numbers
+ */
+ public List<PortNumber> getPorts() {
+ return ports;
+ }
+
+ /**
+ * Returns the label stack associated with the policy group params.
+ *
+ * @return list of integers
+ */
+ public List<Integer> getLabelStack() {
+ return labelStack;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (PortNumber port:ports) {
+ combinedHash = combinedHash + port.hashCode();
+ }
+ combinedHash = combinedHash + Objects.hash(labelStack);
+ result = 31 * result + combinedHash;
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof PolicyGroupParams) {
+ PolicyGroupParams that = (PolicyGroupParams) obj;
+ boolean result = this.labelStack.equals(that.labelStack);
+ result = result &&
+ this.ports.containsAll(that.ports) &&
+ that.ports.containsAll(this.ports);
+ return result;
+ }
+
+ return false;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
index eba2c6c..9248625 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
@@ -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 @@
}
return false;
}
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("description", super.toString())
+ .add("groupid", id)
+ .add("state", state)
+ .toString();
+ }
}
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
index 931cc71..ff1271e 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
@@ -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 @@
return false;
}
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("type", type)
+ .add("treatment", treatment)
+ .toString();
+ }
}
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
index 8d374c1..385a5d6 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
@@ -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 @@
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
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
index 5ca8f30..26cd5f6 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
@@ -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 @@
return false;
}
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("buckets", buckets)
+ .toString();
+ }
}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupStore.java b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
index 6803b58..e7bf4f8 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
@@ -132,8 +132,9 @@
* 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.
diff --git a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
index 4cb21e5..1490b0b 100644
--- a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
+++ b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
@@ -32,6 +32,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
}
}
}
diff --git a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
index 7099464..2fe5e4b 100644
--- a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
@@ -32,6 +32,7 @@
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 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 @@
*/
@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 @@
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 @@
providerService.pushGroupMetrics(DID, groupEntries);
// First group metrics would trigger the device audit completion
// post which all pending group requests are also executed.
+ TestGroupKey key = new TestGroupKey("group1BeforeAudit");
Group createdGroup = groupService.getGroup(DID, key);
int createdGroupId = createdGroup.id().id();
assertNotEquals(gId1.id(), createdGroupId);
assertNotEquals(gId2.id(), createdGroupId);
+
List<GroupOperation> expectedGroupOps = Arrays.asList(
GroupOperation.createDeleteGroupOperation(gId1,
Group.Type.SELECT),
GroupOperation.createAddGroupOperation(
createdGroup.id(),
Group.Type.SELECT,
- groupBuckets));
+ createdGroup.buckets()));
internalProvider.validate(DID, expectedGroupOps);
+ }
- group1 = createSouthboundGroupEntry(gId1,
+ // Test AUDIT process with extraneous groups and missing groups
+ private void testAuditWithExtraneousMissingGroups() {
+ PortNumber[] ports1 = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ PortNumber[] ports2 = {PortNumber.portNumber(41),
+ PortNumber.portNumber(42)};
+ GroupId gId1 = new DefaultGroupId(1);
+ Group group1 = createSouthboundGroupEntry(gId1,
Arrays.asList(ports1),
0);
- group2 = createSouthboundGroupEntry(gId2,
+ GroupId gId2 = new DefaultGroupId(2);
+ Group group2 = createSouthboundGroupEntry(gId2,
Arrays.asList(ports2),
0);
- groupEntries = Arrays.asList(group1, group2);
+ List<Group> groupEntries = Arrays.asList(group1, group2);
providerService.pushGroupMetrics(DID, groupEntries);
- expectedGroupOps = Arrays.asList(
+ TestGroupKey key = new TestGroupKey("group1BeforeAudit");
+ Group createdGroup = groupService.getGroup(DID, key);
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
GroupOperation.createDeleteGroupOperation(gId1,
Group.Type.SELECT),
GroupOperation.createDeleteGroupOperation(gId2,
Group.Type.SELECT),
GroupOperation.createAddGroupOperation(createdGroup.id(),
Group.Type.SELECT,
- groupBuckets));
+ createdGroup.buckets()));
internalProvider.validate(DID, expectedGroupOps);
+ }
+ // Test AUDIT with confirmed groups
+ private void testAuditWithConfirmedGroups() {
+ TestGroupKey key = new TestGroupKey("group1BeforeAudit");
+ Group createdGroup = groupService.getGroup(DID, key);
createdGroup = new DefaultGroup(createdGroup.id(),
DID,
Group.Type.SELECT,
- groupBuckets);
- groupEntries = Arrays.asList(createdGroup);
+ createdGroup.buckets());
+ List<Group> groupEntries = Arrays.asList(createdGroup);
providerService.pushGroupMetrics(DID, groupEntries);
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
+ }
- // Test group add bucket operations
+ // Test group add bucket operations
+ private void testAddBuckets() {
TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
+
+ TestGroupKey prevKey = new TestGroupKey("group1BeforeAudit");
+ Group createdGroup = groupService.getGroup(DID, prevKey);
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ buckets.addAll(createdGroup.buckets().buckets());
+
PortNumber[] addPorts = {PortNumber.portNumber(51),
PortNumber.portNumber(52)};
- outPorts.clear();
+ List<PortNumber> outPorts = new ArrayList<PortNumber>();
outPorts.addAll(Arrays.asList(addPorts));
List<GroupBucket> addBuckets = new ArrayList<GroupBucket>();
for (PortNumber portNumber: outPorts) {
@@ -253,26 +310,34 @@
}
GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
groupService.addBucketsToGroup(DID,
- key,
+ prevKey,
groupAddBuckets,
addKey,
appId);
GroupBuckets updatedBuckets = new GroupBuckets(buckets);
- expectedGroupOps = Arrays.asList(
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
GroupOperation.createModifyGroupOperation(createdGroup.id(),
Group.Type.SELECT,
updatedBuckets));
internalProvider.validate(DID, expectedGroupOps);
Group existingGroup = groupService.getGroup(DID, addKey);
- groupEntries = Arrays.asList(existingGroup);
+ List<Group> groupEntries = Arrays.asList(existingGroup);
providerService.pushGroupMetrics(DID, groupEntries);
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
+ }
- // Test group remove bucket operations
+ // Test group remove bucket operations
+ private void testRemoveBuckets() {
TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
+
+ TestGroupKey prevKey = new TestGroupKey("group1AddBuckets");
+ Group createdGroup = groupService.getGroup(DID, prevKey);
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ buckets.addAll(createdGroup.buckets().buckets());
+
PortNumber[] removePorts = {PortNumber.portNumber(31),
PortNumber.portNumber(32)};
- outPorts.clear();
+ List<PortNumber> outPorts = new ArrayList<PortNumber>();
outPorts.addAll(Arrays.asList(removePorts));
List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>();
for (PortNumber portNumber: outPorts) {
@@ -289,28 +354,32 @@
}
GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
groupService.removeBucketsFromGroup(DID,
- addKey,
+ prevKey,
groupRemoveBuckets,
removeKey,
appId);
- updatedBuckets = new GroupBuckets(buckets);
- expectedGroupOps = Arrays.asList(
+ GroupBuckets updatedBuckets = new GroupBuckets(buckets);
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
GroupOperation.createModifyGroupOperation(createdGroup.id(),
Group.Type.SELECT,
updatedBuckets));
internalProvider.validate(DID, expectedGroupOps);
- existingGroup = groupService.getGroup(DID, removeKey);
- groupEntries = Arrays.asList(existingGroup);
+ Group existingGroup = groupService.getGroup(DID, removeKey);
+ List<Group> groupEntries = Arrays.asList(existingGroup);
providerService.pushGroupMetrics(DID, groupEntries);
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
+ }
- // Test group remove operations
- groupService.removeGroup(DID, removeKey, appId);
- expectedGroupOps = Arrays.asList(
- GroupOperation.createDeleteGroupOperation(createdGroup.id(),
+ // Test group remove operations
+ private void testRemoveGroup() {
+ TestGroupKey currKey = new TestGroupKey("group1RemoveBuckets");
+ Group existingGroup = groupService.getGroup(DID, currKey);
+ groupService.removeGroup(DID, currKey, appId);
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
+ GroupOperation.createDeleteGroupOperation(existingGroup.id(),
Group.Type.SELECT));
internalProvider.validate(DID, expectedGroupOps);
- groupEntries = Collections.emptyList();
+ List<Group> groupEntries = Collections.emptyList();
providerService.pushGroupMetrics(DID, groupEntries);
internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
}
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java
index 9258dcc..e2adf8c 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleGroupStore.java
@@ -261,6 +261,11 @@
}
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 @@
}
@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;
}
}
diff --git a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java
index f890754..712adcb 100644
--- a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleGroupStoreTest.java
@@ -19,6 +19,7 @@
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 @@
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 @@
@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 @@
}
assertEquals(1, groupCount);
simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
+ }
- // Testing addOrUpdateGroupEntry operation from southbound
+ // 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 updateGroupDescription for ADD operation from northbound
- TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
+ // Testing addOrUpdateGroupEntry operation from southbound
+ private void testUpdateGroupEntryFromSB(TestGroupKey currKey) {
+ Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
+
+ InternalGroupStoreDelegate updateGroupEntryDelegate =
+ new InternalGroupStoreDelegate(currKey,
+ existingGroup.buckets(),
+ GroupEvent.Type.GROUP_UPDATED);
+ simpleGroupStore.setDelegate(updateGroupEntryDelegate);
+ simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
+ simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
+ }
+
+ // Testing updateGroupDescription for ADD operation from northbound
+ private void testAddBuckets(TestGroupKey currKey, TestGroupKey addKey) {
+ Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ buckets.addAll(existingGroup.buckets().buckets());
+
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 @@
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");
+ // Testing updateGroupDescription for REMOVE operation from northbound
+ private void testRemoveBuckets(TestGroupKey currKey, TestGroupKey removeKey) {
+ Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
+ List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+ buckets.addAll(existingGroup.buckets().buckets());
+
List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
- toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
- toRemoveBuckets.add(updatedGroupBuckets.buckets().get(1));
+
+ // There should be 4 buckets in the current group
+ toRemoveBuckets.add(buckets.remove(0));
+ toRemoveBuckets.add(buckets.remove(1));
GroupBuckets toRemoveGroupBuckets = new GroupBuckets(toRemoveBuckets);
- List<GroupBucket> remainingBuckets = new ArrayList<GroupBucket>();
- remainingBuckets.add(updatedGroupBuckets.buckets().get(2));
- remainingBuckets.add(updatedGroupBuckets.buckets().get(3));
- GroupBuckets remainingGroupBuckets = new GroupBuckets(remainingBuckets);
+
+ GroupBuckets remainingGroupBuckets = new GroupBuckets(buckets);
InternalGroupStoreDelegate removeGroupDescDelegate =
new InternalGroupStoreDelegate(removeKey,
remainingGroupBuckets,
GroupEvent.Type.GROUP_UPDATE_REQUESTED);
simpleGroupStore.setDelegate(removeGroupDescDelegate);
simpleGroupStore.updateGroupDescription(D1,
- addKey,
+ currKey,
UpdateType.REMOVE,
toRemoveGroupBuckets,
removeKey);
simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
+ }
- // Testing getGroup operation
- Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
- checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
-
- // Testing addOrUpdateGroupEntry operation from southbound
- InternalGroupStoreDelegate updateGroupEntryDelegate =
- new InternalGroupStoreDelegate(removeKey,
- remainingGroupBuckets,
- GroupEvent.Type.GROUP_UPDATED);
- simpleGroupStore.setDelegate(updateGroupEntryDelegate);
- simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
- simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
-
- // Testing deleteGroupDescription operation from northbound
+ // Testing deleteGroupDescription operation from northbound
+ private void testDeleteGroup(TestGroupKey currKey) {
+ Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
InternalGroupStoreDelegate deleteGroupDescDelegate =
- new InternalGroupStoreDelegate(removeKey,
- remainingGroupBuckets,
+ new InternalGroupStoreDelegate(currKey,
+ existingGroup.buckets(),
GroupEvent.Type.GROUP_REMOVE_REQUESTED);
simpleGroupStore.setDelegate(deleteGroupDescDelegate);
- simpleGroupStore.deleteGroupDescription(D1, removeKey);
+ simpleGroupStore.deleteGroupDescription(D1, currKey);
simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
+ }
- // Testing removeGroupEntry operation from southbound
+ // Testing removeGroupEntry operation from southbound
+ private void testRemoveGroupFromSB(TestGroupKey currKey) {
+ Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
InternalGroupStoreDelegate removeGroupEntryDelegate =
- new InternalGroupStoreDelegate(removeKey,
- remainingGroupBuckets,
+ new InternalGroupStoreDelegate(currKey,
+ existingGroup.buckets(),
GroupEvent.Type.GROUP_REMOVED);
simpleGroupStore.setDelegate(removeGroupEntryDelegate);
simpleGroupStore.removeGroupEntry(existingGroup);
// Testing getGroup operation
- existingGroup = simpleGroupStore.getGroup(D1, removeKey);
+ existingGroup = simpleGroupStore.getGroup(D1, currKey);
assertEquals(null, existingGroup);
assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1)));
assertEquals(0, simpleGroupStore.getGroupCount(D1));
simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
-
-
}
@Test
public void testGroupOperationFailure() {
- simpleGroupStore.deviceInitialAuditCompleted(D1);
+ simpleGroupStore.deviceInitialAuditCompleted(D1, true);
ApplicationId appId =
new DefaultApplicationId(2, "org.groupstore.test");
@@ -408,8 +454,6 @@
GroupEvent.Type.GROUP_REMOVE_FAILED);
simpleGroupStore.setDelegate(checkGroupDelFailureDelegate);
simpleGroupStore.groupOperationFailed(D1, groupDelOp);
-
-
}
}