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
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onosproject</groupId>
24 + <artifactId>onos-apps</artifactId>
25 + <version>1.1.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-grouphandler</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>ONOS sample application using group service</description>
33 +
34 + <dependencies>
35 + <dependency>
36 + <groupId>org.onosproject</groupId>
37 + <artifactId>onos-api</artifactId>
38 + <version>${project.version}</version>
39 + </dependency>
40 + <dependency>
41 + <groupId>org.onosproject</groupId>
42 + <artifactId>onlab-osgi</artifactId>
43 + <version>${project.version}</version>
44 + </dependency>
45 + <dependency>
46 + <groupId>org.onosproject</groupId>
47 + <artifactId>onlab-nio</artifactId>
48 + <version>${project.version}</version>
49 + </dependency>
50 + <dependency>
51 + <groupId>org.onosproject</groupId>
52 + <artifactId>onlab-netty</artifactId>
53 + <version>${project.version}</version>
54 + </dependency>
55 + <dependency>
56 + <groupId>org.apache.karaf.shell</groupId>
57 + <artifactId>org.apache.karaf.shell.console</artifactId>
58 + </dependency>
59 + <dependency>
60 + <groupId>org.onosproject</groupId>
61 + <artifactId>onlab-misc</artifactId>
62 + </dependency>
63 + <dependency>
64 + <groupId>com.google.guava</groupId>
65 + <artifactId>guava</artifactId>
66 + </dependency>
67 + <dependency>
68 + <groupId>org.osgi</groupId>
69 + <artifactId>org.osgi.core</artifactId>
70 + </dependency>
71 + </dependencies>
72 +</project>
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import java.util.Arrays;
19 +import java.util.HashSet;
20 +import java.util.List;
21 +import java.util.Set;
22 +
23 +import org.onosproject.core.ApplicationId;
24 +import org.onosproject.net.DeviceId;
25 +import org.onosproject.net.Link;
26 +import org.onosproject.net.flow.DefaultTrafficTreatment;
27 +import org.onosproject.net.flow.TrafficTreatment;
28 +import org.onosproject.net.group.DefaultGroupBucket;
29 +import org.onosproject.net.group.GroupBucket;
30 +import org.onosproject.net.group.GroupBuckets;
31 +import org.onosproject.net.group.GroupService;
32 +import org.onosproject.net.link.LinkService;
33 +
34 +/**
35 + * Default ECMP group handler creation module for an edge device.
36 + * This component creates a set of ECMP groups for every neighbor
37 + * that this device is connected to.
38 + * For example, consider a network of 4 devices: D0 (Segment ID: 100),
39 + * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
40 + * where D0 and D3 are edge devices and D1 and D2 are transit devices.
41 + * Assume device D0 is connected to 2 neighbors (D1 and D2 ).
42 + * The following groups will be created in D0:
43 + * 1) all ports to D1 + with no label push,
44 + * 2) all ports to D1 + with label 102 pushed,
45 + * 3) all ports to D1 + with label 103 pushed,
46 + * 4) all ports to D2 + with no label push,
47 + * 5) all ports to D2 + with label 101 pushed,
48 + * 6) all ports to D2 + with label 103 pushed,
49 + * 7) all ports to D1 and D2 + with label 103 pushed
50 + */
51 +public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
52 +
53 + protected DefaultEdgeGroupHandler(DeviceId deviceId,
54 + ApplicationId appId,
55 + DeviceProperties config,
56 + LinkService linkService,
57 + GroupService groupService) {
58 + super(deviceId, appId, config, linkService, groupService);
59 + }
60 +
61 + @Override
62 + public void createGroups() {
63 + log.debug("Creating default groups "
64 + + "for edge device {}", deviceId);
65 + Set<DeviceId> neighbors = devicePortMap.keySet();
66 + if (neighbors == null || neighbors.isEmpty()) {
67 + return;
68 + }
69 +
70 + // Create all possible Neighbor sets from this router
71 + Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(neighbors);
72 + log.trace("createGroupsAtEdgeRouter: The size of neighbor powerset "
73 + + "for sw {} is {}", deviceId, powerSet.size());
74 + Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
75 + for (Set<DeviceId> combo : powerSet) {
76 + if (combo.isEmpty()) {
77 + continue;
78 + }
79 + List<Integer> groupSegmentIds =
80 + getSegmentIdsTobePairedWithNeighborSet(combo);
81 + for (Integer sId : groupSegmentIds) {
82 + NeighborSet ns = new NeighborSet(combo, sId);
83 + log.trace("createGroupsAtEdgeRouter: sw {} "
84 + + "combo {} sId {} ns {}",
85 + deviceId, combo, sId, ns);
86 + nsSet.add(ns);
87 + }
88 + }
89 + log.trace("createGroupsAtEdgeRouter: The neighborset "
90 + + "with label for sw {} is {}",
91 + deviceId, nsSet);
92 +
93 + createGroupsFromNeighborsets(nsSet);
94 + }
95 +
96 + @Override
97 + protected void newNeighbor(Link newNeighborLink) {
98 + log.debug("New Neighbor: Updating groups "
99 + + "for edge device {}", deviceId);
100 + // Recompute neighbor power set
101 + addNeighborAtPort(newNeighborLink.dst().deviceId(),
102 + newNeighborLink.src().port());
103 + // Compute new neighbor sets due to the addition of new neighbor
104 + Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
105 + newNeighborLink.dst().deviceId(),
106 + devicePortMap.keySet());
107 + createGroupsFromNeighborsets(nsSet);
108 + }
109 +
110 + @Override
111 + protected void newPortToExistingNeighbor(Link newNeighborLink) {
112 + log.debug("New port to existing neighbor: Updating "
113 + + "groups for edge device {}", deviceId);
114 + addNeighborAtPort(newNeighborLink.dst().deviceId(),
115 + newNeighborLink.src().port());
116 + Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
117 + newNeighborLink.dst().deviceId(),
118 + devicePortMap.keySet());
119 + for (NeighborSet ns : nsSet) {
120 + // Create the new bucket to be updated
121 + TrafficTreatment.Builder tBuilder =
122 + DefaultTrafficTreatment.builder();
123 + tBuilder.setOutput(newNeighborLink.src().port())
124 + .setEthDst(deviceConfig.getDeviceMac(
125 + newNeighborLink.dst().deviceId()))
126 + .setEthSrc(nodeMacAddr)
127 + .pushMpls()
128 + .setMpls(ns.getEdgeLabel());
129 + GroupBucket updatedBucket = DefaultGroupBucket.
130 + createSelectGroupBucket(tBuilder.build());
131 + GroupBuckets updatedBuckets = new GroupBuckets(
132 + Arrays.asList(updatedBucket));
133 + log.debug("newPortToExistingNeighborAtEdgeRouter: "
134 + + "groupService.addBucketsToGroup for neighborset{}", ns);
135 + groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
136 + }
137 + }
138 +
139 + @Override
140 + protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
141 + DeviceId impactedNeighbor,
142 + Set<DeviceId> updatedNeighbors) {
143 + Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
144 +
145 + Set<DeviceId> tmp = new HashSet<DeviceId>();
146 + tmp.addAll(updatedNeighbors);
147 + tmp.remove(impactedNeighbor);
148 + Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
149 +
150 + // Compute the impacted neighbor sets
151 + powerSet.removeAll(tmpPowerSet);
152 +
153 + Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
154 + for (Set<DeviceId> combo : powerSet) {
155 + if (combo.isEmpty()) {
156 + continue;
157 + }
158 + List<Integer> groupSegmentIds =
159 + getSegmentIdsTobePairedWithNeighborSet(combo);
160 + for (Integer sId : groupSegmentIds) {
161 + NeighborSet ns = new NeighborSet(combo, sId);
162 + log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
163 + + "combo {} sId {} ns {}",
164 + deviceId, combo, sId, ns);
165 + nsSet.add(ns);
166 + }
167 + }
168 + log.trace("computeImpactedNeighborsetForPortEvent: The neighborset "
169 + + "with label for sw {} is {}",
170 + deviceId, nsSet);
171 + return nsSet;
172 + }
173 +
174 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import static com.google.common.base.Preconditions.checkNotNull;
19 +import static org.slf4j.LoggerFactory.getLogger;
20 +
21 +import java.util.ArrayList;
22 +import java.util.Arrays;
23 +import java.util.HashMap;
24 +import java.util.HashSet;
25 +import java.util.List;
26 +import java.util.Set;
27 +
28 +import org.onlab.packet.MacAddress;
29 +import org.onosproject.core.ApplicationId;
30 +import org.onosproject.net.DeviceId;
31 +import org.onosproject.net.Link;
32 +import org.onosproject.net.PortNumber;
33 +import org.onosproject.net.flow.DefaultTrafficTreatment;
34 +import org.onosproject.net.flow.TrafficTreatment;
35 +import org.onosproject.net.group.DefaultGroupBucket;
36 +import org.onosproject.net.group.DefaultGroupDescription;
37 +import org.onosproject.net.group.Group;
38 +import org.onosproject.net.group.GroupBucket;
39 +import org.onosproject.net.group.GroupBuckets;
40 +import org.onosproject.net.group.GroupDescription;
41 +import org.onosproject.net.group.GroupEvent;
42 +import org.onosproject.net.group.GroupKey;
43 +import org.onosproject.net.group.GroupListener;
44 +import org.onosproject.net.group.GroupService;
45 +import org.onosproject.net.link.LinkService;
46 +import org.slf4j.Logger;
47 +
48 +/**
49 + * Default ECMP group handler creation module. This
50 + * component creates a set of ECMP groups for every neighbor
51 + * that this device is connected to based on whether the
52 + * current device is an edge device or a transit device.
53 + */
54 +public class DefaultGroupHandler {
55 + protected final Logger log = getLogger(getClass());
56 +
57 + protected final DeviceId deviceId;
58 + protected final ApplicationId appId;
59 + protected final DeviceProperties deviceConfig;
60 + protected final List<Integer> allSegmentIds;
61 + protected final int nodeSegmentId;
62 + protected final boolean isEdgeRouter;
63 + protected final MacAddress nodeMacAddr;
64 + protected LinkService linkService;
65 + protected GroupService groupService;
66 +
67 + protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
68 + new HashMap<DeviceId, Set<PortNumber>>();
69 + protected HashMap<PortNumber, DeviceId> portDeviceMap =
70 + new HashMap<PortNumber, DeviceId>();
71 +
72 + private GroupListener listener = new InternalGroupListener();
73 +
74 + protected DefaultGroupHandler(DeviceId deviceId,
75 + ApplicationId appId,
76 + DeviceProperties config,
77 + LinkService linkService,
78 + GroupService groupService) {
79 + this.deviceId = checkNotNull(deviceId);
80 + this.appId = checkNotNull(appId);
81 + this.deviceConfig = checkNotNull(config);
82 + this.linkService = checkNotNull(linkService);
83 + this.groupService = checkNotNull(groupService);
84 + allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
85 + nodeSegmentId = config.getSegmentId(deviceId);
86 + isEdgeRouter = config.isEdgeDevice(deviceId);
87 + nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
88 +
89 + this.groupService.addListener(listener);
90 +
91 + populateNeighborMaps();
92 + }
93 +
94 + /**
95 + * Creates a group handler object based on the type of device. If
96 + * device is of edge type it returns edge group handler, else it
97 + * returns transit group handler.
98 + *
99 + * @param deviceId device identifier
100 + * @param appId application identifier
101 + * @param config interface to retrieve the device properties
102 + * @param linkService link service object
103 + * @param groupService group service object
104 + * @return default group handler type
105 + */
106 + public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
107 + ApplicationId appId,
108 + DeviceProperties config,
109 + LinkService linkService,
110 + GroupService groupService) {
111 + if (config.isEdgeDevice(deviceId)) {
112 + return new DefaultEdgeGroupHandler(deviceId,
113 + appId,
114 + config,
115 + linkService,
116 + groupService);
117 + } else {
118 + return new DefaultTransitGroupHandler(deviceId,
119 + appId,
120 + config,
121 + linkService,
122 + groupService);
123 + }
124 + }
125 +
126 + /**
127 + * Creates the auto created groups for this device based on the
128 + * current snapshot of the topology.
129 + */
130 + //Empty implementations to be overridden by derived classes
131 + public void createGroups() {
132 + }
133 +
134 + /**
135 + * Performs group creation or update procedures when a new link
136 + * is discovered on this device.
137 + *
138 + * @param newLink new neighbor link
139 + */
140 + public void linkUp(Link newLink) {
141 + if (newLink.type() != Link.Type.DIRECT) {
142 + log.warn("linkUp: unknown link type");
143 + return;
144 + }
145 +
146 + if (!newLink.src().deviceId().equals(deviceId)) {
147 + log.warn("linkUp: deviceId{} doesn't match with link src{}",
148 + deviceId,
149 + newLink.src().deviceId());
150 + return;
151 + }
152 +
153 + log.debug("Device {} linkUp at local port {} to neighbor {}",
154 + deviceId, newLink.src().port(), newLink.dst().deviceId());
155 + if (devicePortMap.get(newLink.dst().deviceId()) == null) {
156 + // New Neighbor
157 + newNeighbor(newLink);
158 + } else {
159 + // Old Neighbor
160 + newPortToExistingNeighbor(newLink);
161 + }
162 + }
163 +
164 + /**
165 + * Performs group recovery procedures when a port goes down
166 + * on this device.
167 + *
168 + * @param port port number that has gone down
169 + */
170 + public void portDown(PortNumber port) {
171 + if (portDeviceMap.get(port) == null) {
172 + log.warn("portDown: unknown port");
173 + return;
174 + }
175 + log.debug("Device {} portDown {} to neighbor {}",
176 + deviceId, port, portDeviceMap.get(port));
177 + Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
178 + portDeviceMap.get(port),
179 + devicePortMap.keySet());
180 + for (NeighborSet ns : nsSet) {
181 + // Create the bucket to be removed
182 + TrafficTreatment.Builder tBuilder =
183 + DefaultTrafficTreatment.builder();
184 + tBuilder.setOutput(port)
185 + .setEthDst(deviceConfig.getDeviceMac(
186 + portDeviceMap.get(port)))
187 + .setEthSrc(nodeMacAddr)
188 + .pushMpls()
189 + .setMpls(ns.getEdgeLabel());
190 + GroupBucket removeBucket = DefaultGroupBucket.
191 + createSelectGroupBucket(tBuilder.build());
192 + GroupBuckets removeBuckets = new GroupBuckets(
193 + Arrays.asList(removeBucket));
194 + log.debug("portDown in device{}: "
195 + + "groupService.removeBucketsFromGroup "
196 + + "for neighborset{}", deviceId, ns);
197 + groupService.removeBucketsFromGroup(deviceId,
198 + ns,
199 + removeBuckets,
200 + ns,
201 + appId);
202 + }
203 +
204 + devicePortMap.get(portDeviceMap.get(port)).remove(port);
205 + portDeviceMap.remove(port);
206 + }
207 +
208 + /**
209 + * Returns a group associated with the key.
210 + *
211 + * @param key cookie associated with the group
212 + * @return group if found or null
213 + */
214 + public Group getGroup(GroupKey key) {
215 + return groupService.getGroup(deviceId, key);
216 + }
217 +
218 + //Empty implementation
219 + protected void newNeighbor(Link newLink) {
220 + }
221 +
222 + //Empty implementation
223 + protected void newPortToExistingNeighbor(Link newLink) {
224 + }
225 +
226 + //Empty implementation
227 + protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
228 + DeviceId impactedNeighbor,
229 + Set<DeviceId> updatedNeighbors) {
230 + return null;
231 + }
232 +
233 + private void populateNeighborMaps() {
234 + Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
235 + for (Link link:outgoingLinks) {
236 + if (link.type() != Link.Type.DIRECT) {
237 + continue;
238 + }
239 + addNeighborAtPort(link.dst().deviceId(), link.src().port());
240 + }
241 + }
242 +
243 + protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
244 + // Update DeviceToPort database
245 + log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
246 + deviceId, neighborId, portToNeighbor);
247 + if (devicePortMap.get(neighborId) != null) {
248 + devicePortMap.get(neighborId).add(portToNeighbor);
249 + } else {
250 + Set<PortNumber> ports = new HashSet<PortNumber>();
251 + ports.add(portToNeighbor);
252 + devicePortMap.put(neighborId, ports);
253 + }
254 +
255 + // Update portToDevice database
256 + if (portDeviceMap.get(portToNeighbor) == null) {
257 + portDeviceMap.put(portToNeighbor, neighborId);
258 + }
259 + }
260 +
261 + protected Set<Set<DeviceId>>
262 + getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
263 + List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
264 + Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
265 + // get the number of elements in the neighbors
266 + int elements = list.size();
267 + // the number of members of a power set is 2^n
268 + // including the empty set
269 + int powerElements = (1 << elements);
270 +
271 + // run a binary counter for the number of power elements
272 + // NOTE: Exclude empty set
273 + for (long i = 1; i < powerElements; i++) {
274 + Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
275 + for (int j = 0; j < elements; j++) {
276 + if ((i >> j) % 2 == 1) {
277 + neighborSubSet.add(list.get(j));
278 + }
279 + }
280 + sets.add(neighborSubSet);
281 + }
282 + return sets;
283 + }
284 +
285 + private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
286 + return (deviceConfig.getSegmentId(deviceId) == sId);
287 + }
288 +
289 + protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
290 + Set<DeviceId> neighbors) {
291 +
292 + List<Integer> nsSegmentIds = new ArrayList<Integer>();
293 +
294 + // Add one entry for "no label" (-1) to the list if
295 + // dpid list has not more than one node/neighbor as
296 + // there will never be a case a packet going to more than one
297 + // neighbor without a label at an edge router
298 + if (neighbors.size() == 1) {
299 + nsSegmentIds.add(-1);
300 + }
301 + // Filter out SegmentIds matching with the
302 + // nodes in the combo
303 + for (Integer sId : allSegmentIds) {
304 + if (sId.equals(nodeSegmentId)) {
305 + continue;
306 + }
307 + boolean filterOut = false;
308 + // Check if the edge label being set is of
309 + // any node in the Neighbor set
310 + for (DeviceId deviceId : neighbors) {
311 + if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
312 + filterOut = true;
313 + break;
314 + }
315 + }
316 + if (!filterOut) {
317 + nsSegmentIds.add(sId);
318 + }
319 + }
320 + return nsSegmentIds;
321 + }
322 +
323 + protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
324 + for (NeighborSet ns : nsSet) {
325 + // Create the bucket array from the neighbor set
326 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
327 + for (DeviceId d : ns.getDeviceIds()) {
328 + for (PortNumber sp : devicePortMap.get(d)) {
329 + TrafficTreatment.Builder tBuilder =
330 + DefaultTrafficTreatment.builder();
331 + tBuilder.setOutput(sp)
332 + .setEthDst(deviceConfig.getDeviceMac(d))
333 + .setEthSrc(nodeMacAddr)
334 + .pushMpls()
335 + .setMpls(ns.getEdgeLabel());
336 + buckets.add(DefaultGroupBucket.createSelectGroupBucket(
337 + tBuilder.build()));
338 + }
339 + }
340 + GroupBuckets groupBuckets = new GroupBuckets(buckets);
341 + GroupDescription newGroupDesc = new DefaultGroupDescription(
342 + deviceId,
343 + Group.Type.SELECT,
344 + groupBuckets,
345 + ns,
346 + appId);
347 + log.debug("createGroupsFromNeighborsets: "
348 + + "groupService.addGroup for neighborset{}", ns);
349 + groupService.addGroup(newGroupDesc);
350 + }
351 + }
352 +
353 + protected void handleGroupEvent(GroupEvent event) {
354 + switch (event.type()) {
355 + case GROUP_ADDED:
356 + log.debug("Received GROUP_ADDED from group service "
357 + + "for device {} with group key{} with id{}",
358 + event.subject().deviceId(),
359 + event.subject().appCookie(),
360 + event.subject().id());
361 + break;
362 + case GROUP_UPDATED:
363 + log.trace("Received GROUP_UPDATED from group service "
364 + + "for device {} with group key{} with id{}",
365 + event.subject().deviceId(),
366 + event.subject().appCookie(),
367 + event.subject().id());
368 + break;
369 + case GROUP_REMOVED:
370 + log.debug("Received GROUP_REMOVED from group service "
371 + + "for device {} with group key{} with id{}",
372 + event.subject().deviceId(),
373 + event.subject().appCookie(),
374 + event.subject().id());
375 + break;
376 + default:
377 + break;
378 + }
379 + }
380 +
381 + private class InternalGroupListener implements GroupListener {
382 +
383 + @Override
384 + public void event(GroupEvent event) {
385 + handleGroupEvent(event);
386 + }
387 + }
388 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import static org.slf4j.LoggerFactory.getLogger;
19 +
20 +import java.util.Arrays;
21 +import java.util.HashMap;
22 +import java.util.List;
23 +
24 +import org.apache.felix.scr.annotations.Activate;
25 +import org.apache.felix.scr.annotations.Component;
26 +import org.apache.felix.scr.annotations.Deactivate;
27 +import org.apache.felix.scr.annotations.Reference;
28 +import org.apache.felix.scr.annotations.ReferenceCardinality;
29 +import org.onlab.packet.MacAddress;
30 +import org.onosproject.core.ApplicationId;
31 +import org.onosproject.core.CoreService;
32 +import org.onosproject.net.Device;
33 +import org.onosproject.net.DeviceId;
34 +import org.onosproject.net.device.DeviceEvent;
35 +import org.onosproject.net.device.DeviceListener;
36 +import org.onosproject.net.device.DeviceService;
37 +import org.onosproject.net.group.GroupService;
38 +import org.onosproject.net.link.LinkEvent;
39 +import org.onosproject.net.link.LinkListener;
40 +import org.onosproject.net.link.LinkService;
41 +import org.onosproject.net.topology.TopologyService;
42 +import org.slf4j.Logger;
43 +
44 +/**
45 + * Sample application to verify group subsystem end to end.
46 + * This application expects a network of maximum of six connected
47 + * devices for the test to work. For every device in the network,
48 + * this test application launches a default group handler function
49 + * that creates ECMP groups for every neighbor the device is
50 + * connected to.
51 + */
52 +@Component(immediate = true)
53 +public class DefaultGroupHandlerApp {
54 +
55 + private final Logger log = getLogger(getClass());
56 +
57 + private final DeviceProperties config = new DeviceConfiguration();
58 + private ApplicationId appId;
59 + private HashMap<DeviceId, DefaultGroupHandler> dghMap =
60 + new HashMap<DeviceId, DefaultGroupHandler>();
61 +
62 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 + protected TopologyService topologyService;
64 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 + protected DeviceService deviceService;
66 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 + protected LinkService linkService;
68 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 + protected GroupService groupService;
70 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 + protected CoreService coreService;
72 +
73 + private DeviceListener deviceListener = new InternalDeviceListener();
74 + private LinkListener linkListener = new InternalLinkListener();
75 +
76 + @Activate
77 + public void activate() {
78 + appId = coreService.registerApplication("org.onosproject.defaultgrouphandler");
79 + log.info("DefaultGroupHandlerApp Activating");
80 + deviceService.addListener(deviceListener);
81 + linkService.addListener(linkListener);
82 + for (Device device: deviceService.getDevices()) {
83 + log.debug("Initiating default group handling for {}", device.id());
84 + DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(device.id(),
85 + appId,
86 + config,
87 + linkService,
88 + groupService);
89 + dgh.createGroups();
90 + dghMap.put(device.id(), dgh);
91 + }
92 + log.info("Activated");
93 + }
94 +
95 + @Deactivate
96 + public void deactivate() {
97 + dghMap.clear();
98 + }
99 +
100 + public class DeviceConfiguration implements DeviceProperties {
101 + private final List<Integer> allSegmentIds =
102 + Arrays.asList(101, 102, 103, 104, 105, 106);
103 + private HashMap<DeviceId, Integer> deviceSegmentIdMap =
104 + new HashMap<DeviceId, Integer>() {
105 + {
106 + put(DeviceId.deviceId("of:0000000000000001"), 101);
107 + put(DeviceId.deviceId("of:0000000000000002"), 102);
108 + put(DeviceId.deviceId("of:0000000000000003"), 103);
109 + put(DeviceId.deviceId("of:0000000000000004"), 104);
110 + put(DeviceId.deviceId("of:0000000000000005"), 105);
111 + put(DeviceId.deviceId("of:0000000000000006"), 106);
112 + }
113 + };
114 + private final HashMap<DeviceId, MacAddress> deviceMacMap =
115 + new HashMap<DeviceId, MacAddress>() {
116 + {
117 + put(DeviceId.deviceId("of:0000000000000001"),
118 + MacAddress.valueOf("00:00:00:00:00:01"));
119 + put(DeviceId.deviceId("of:0000000000000002"),
120 + MacAddress.valueOf("00:00:00:00:00:02"));
121 + put(DeviceId.deviceId("of:0000000000000003"),
122 + MacAddress.valueOf("00:00:00:00:00:03"));
123 + put(DeviceId.deviceId("of:0000000000000004"),
124 + MacAddress.valueOf("00:00:00:00:00:04"));
125 + put(DeviceId.deviceId("of:0000000000000005"),
126 + MacAddress.valueOf("00:00:00:00:00:05"));
127 + put(DeviceId.deviceId("of:0000000000000006"),
128 + MacAddress.valueOf("00:00:00:00:00:06"));
129 + }
130 + };
131 +
132 + @Override
133 + public int getSegmentId(DeviceId deviceId) {
134 + if (deviceSegmentIdMap.get(deviceId) != null) {
135 + log.debug("getSegmentId for device{} is {}",
136 + deviceId,
137 + deviceSegmentIdMap.get(deviceId));
138 + return deviceSegmentIdMap.get(deviceId);
139 + } else {
140 + throw new IllegalStateException();
141 + }
142 + }
143 + @Override
144 + public MacAddress getDeviceMac(DeviceId deviceId) {
145 + if (deviceMacMap.get(deviceId) != null) {
146 + log.debug("getDeviceMac for device{} is {}",
147 + deviceId,
148 + deviceMacMap.get(deviceId));
149 + return deviceMacMap.get(deviceId);
150 + } else {
151 + throw new IllegalStateException();
152 + }
153 + }
154 + @Override
155 + public boolean isEdgeDevice(DeviceId deviceId) {
156 + return true;
157 + }
158 + @Override
159 + public List<Integer> getAllDeviceSegmentIds() {
160 + return allSegmentIds;
161 + }
162 + }
163 +
164 + private class InternalDeviceListener implements DeviceListener {
165 +
166 + @Override
167 + public void event(DeviceEvent event) {
168 + switch (event.type()) {
169 + case DEVICE_ADDED:
170 + log.debug("Initiating default group handling for {}", event.subject().id());
171 + DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(
172 + event.subject().id(),
173 + appId,
174 + config,
175 + linkService,
176 + groupService);
177 + dgh.createGroups();
178 + dghMap.put(event.subject().id(), dgh);
179 + break;
180 + case PORT_REMOVED:
181 + if (dghMap.get(event.subject().id()) != null) {
182 + dghMap.get(event.subject().id()).portDown(event.port().number());
183 + }
184 + break;
185 + default:
186 + break;
187 + }
188 +
189 + }
190 + }
191 +
192 + private class InternalLinkListener implements LinkListener {
193 +
194 + @Override
195 + public void event(LinkEvent event) {
196 + switch (event.type()) {
197 + case LINK_ADDED:
198 + if (dghMap.get(event.subject().src().deviceId()) != null) {
199 + dghMap.get(event.subject().src().deviceId()).linkUp(event.subject());
200 + }
201 + break;
202 + default:
203 + break;
204 + }
205 + }
206 +
207 + }
208 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import java.util.Arrays;
19 +import java.util.HashSet;
20 +import java.util.Set;
21 +
22 +import org.onosproject.core.ApplicationId;
23 +import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.Link;
25 +import org.onosproject.net.flow.DefaultTrafficTreatment;
26 +import org.onosproject.net.flow.TrafficTreatment;
27 +import org.onosproject.net.group.DefaultGroupBucket;
28 +import org.onosproject.net.group.GroupBucket;
29 +import org.onosproject.net.group.GroupBuckets;
30 +import org.onosproject.net.group.GroupService;
31 +import org.onosproject.net.link.LinkService;
32 +
33 +/**
34 + * Default ECMP group handler creation module for a transit device.
35 + * This component creates a set of ECMP groups for every neighbor
36 + * that this device is connected to.
37 + * For example, consider a network of 4 devices: D0 (Segment ID: 100),
38 + * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
39 + * where D0 and D3 are edge devices and D1 and D2 are transit devices.
40 + * Assume transit device D1 is connected to 2 neighbors (D0 and D3 ).
41 + * The following groups will be created in D1:
42 + * 1) all ports to D0 + with no label push,
43 + * 2) all ports to D3 + with no label push,
44 + */
45 +public class DefaultTransitGroupHandler extends DefaultGroupHandler {
46 +
47 + protected DefaultTransitGroupHandler(DeviceId deviceId,
48 + ApplicationId appId,
49 + DeviceProperties config,
50 + LinkService linkService,
51 + GroupService groupService) {
52 + super(deviceId, appId, config, linkService, groupService);
53 + }
54 +
55 + @Override
56 + public void createGroups() {
57 + Set<DeviceId> neighbors = devicePortMap.keySet();
58 + if (neighbors == null || neighbors.isEmpty()) {
59 + return;
60 + }
61 +
62 + // Create all possible Neighbor sets from this router
63 + // NOTE: Avoid any pairings of edge routers only
64 + Set<Set<DeviceId>> sets = getPowerSetOfNeighbors(neighbors);
65 + sets = filterEdgeRouterOnlyPairings(sets);
66 + log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
67 + + "for sw {} is {}", deviceId, sets.size());
68 + Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
69 + for (Set<DeviceId> combo : sets) {
70 + if (combo.isEmpty()) {
71 + continue;
72 + }
73 + NeighborSet ns = new NeighborSet(combo);
74 + log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
75 + deviceId, combo, ns);
76 + nsSet.add(ns);
77 + }
78 + log.debug("createGroupsAtTransitRouter: The neighborset with label "
79 + + "for sw {} is {}", deviceId, nsSet);
80 +
81 + createGroupsFromNeighborsets(nsSet);
82 + }
83 +
84 + @Override
85 + protected void newNeighbor(Link newNeighborLink) {
86 + log.debug("New Neighbor: Updating groups for "
87 + + "transit device {}", deviceId);
88 + // Recompute neighbor power set
89 + addNeighborAtPort(newNeighborLink.dst().deviceId(),
90 + newNeighborLink.src().port());
91 + // Compute new neighbor sets due to the addition of new neighbor
92 + Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
93 + newNeighborLink.dst().deviceId(),
94 + devicePortMap.keySet());
95 + createGroupsFromNeighborsets(nsSet);
96 + }
97 +
98 + @Override
99 + protected void newPortToExistingNeighbor(Link newNeighborLink) {
100 + log.debug("New port to existing neighbor: Updating "
101 + + "groups for transit device {}", deviceId);
102 + addNeighborAtPort(newNeighborLink.dst().deviceId(),
103 + newNeighborLink.src().port());
104 + Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
105 + newNeighborLink.dst().deviceId(),
106 + devicePortMap.keySet());
107 + for (NeighborSet ns : nsSet) {
108 + // Create the new bucket to be updated
109 + TrafficTreatment.Builder tBuilder =
110 + DefaultTrafficTreatment.builder();
111 + tBuilder.setOutput(newNeighborLink.src().port())
112 + .setEthDst(deviceConfig.getDeviceMac(
113 + newNeighborLink.dst().deviceId()))
114 + .setEthSrc(nodeMacAddr)
115 + .pushMpls()
116 + .setMpls(ns.getEdgeLabel());
117 + GroupBucket updatedBucket = DefaultGroupBucket.
118 + createSelectGroupBucket(tBuilder.build());
119 + GroupBuckets updatedBuckets = new GroupBuckets(
120 + Arrays.asList(updatedBucket));
121 + log.debug("newPortToExistingNeighborAtEdgeRouter: "
122 + + "groupService.addBucketsToGroup for neighborset{}", ns);
123 + groupService.addBucketsToGroup(deviceId, ns, updatedBuckets, ns, appId);
124 + }
125 + }
126 +
127 + @Override
128 + protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
129 + DeviceId impactedNeighbor,
130 + Set<DeviceId> updatedNeighbors) {
131 + Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
132 +
133 + Set<DeviceId> tmp = updatedNeighbors;
134 + tmp.remove(impactedNeighbor);
135 + Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
136 +
137 + // Compute the impacted neighbor sets
138 + powerSet.removeAll(tmpPowerSet);
139 +
140 + powerSet = filterEdgeRouterOnlyPairings(powerSet);
141 + Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
142 + for (Set<DeviceId> combo : powerSet) {
143 + if (combo.isEmpty()) {
144 + continue;
145 + }
146 + NeighborSet ns = new NeighborSet(combo);
147 + log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
148 + deviceId, combo, ns);
149 + nsSet.add(ns);
150 + }
151 + log.debug("computeImpactedNeighborsetForPortEvent: The neighborset with label "
152 + + "for sw {} is {}", deviceId, nsSet);
153 +
154 + return nsSet;
155 + }
156 +
157 + private Set<Set<DeviceId>> filterEdgeRouterOnlyPairings(Set<Set<DeviceId>> sets) {
158 + Set<Set<DeviceId>> fiteredSets = new HashSet<Set<DeviceId>>();
159 + for (Set<DeviceId> deviceSubSet : sets) {
160 + if (deviceSubSet.size() > 1) {
161 + boolean avoidEdgeRouterPairing = true;
162 + for (DeviceId device : deviceSubSet) {
163 + if (!deviceConfig.isEdgeDevice(device)) {
164 + avoidEdgeRouterPairing = false;
165 + break;
166 + }
167 + }
168 + if (!avoidEdgeRouterPairing) {
169 + fiteredSets.add(deviceSubSet);
170 + }
171 + } else {
172 + fiteredSets.add(deviceSubSet);
173 + }
174 + }
175 + return fiteredSets;
176 + }
177 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import java.util.List;
19 +
20 +import org.onlab.packet.MacAddress;
21 +import org.onosproject.net.DeviceId;
22 +
23 +/**
24 + * Mechanism through which group handler module retrieves
25 + * the device specific attributes such as segment ID,
26 + * Mac address...etc from group handler applications.
27 + */
28 +public interface DeviceProperties {
29 + /**
30 + * Returns the segment id of a device to be used in group creation.
31 + *
32 + * @param deviceId device identifier
33 + * @return segment id of a device
34 + */
35 + int getSegmentId(DeviceId deviceId);
36 + /**
37 + * Returns the Mac address of a device to be used in group creation.
38 + *
39 + * @param deviceId device identifier
40 + * @return mac address of a device
41 + */
42 + MacAddress getDeviceMac(DeviceId deviceId);
43 + /**
44 + * Indicates whether a device is edge device or transit/core device.
45 + *
46 + * @param deviceId device identifier
47 + * @return boolean
48 + */
49 + boolean isEdgeDevice(DeviceId deviceId);
50 + /**
51 + * Returns all segment IDs to be considered in building auto
52 + *
53 + * created groups.
54 + * @return list of segment IDs
55 + */
56 + List<Integer> getAllDeviceSegmentIds();
57 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import static com.google.common.base.Preconditions.checkNotNull;
19 +
20 +import org.onosproject.net.PortNumber;
21 +import org.onosproject.net.group.GroupKey;
22 +
23 +/**
24 + * Representation of policy group bucket identifier. Not exposed to
25 + * the application and only to be used internally.
26 + */
27 +public class GroupBucketIdentifier {
28 + private int label;
29 + private BucketOutputType type;
30 + private PortNumber outPort;
31 + private GroupKey outGroup;
32 +
33 + protected enum BucketOutputType {
34 + PORT,
35 + GROUP
36 + }
37 +
38 + protected GroupBucketIdentifier(int label,
39 + PortNumber outPort) {
40 + this.label = label;
41 + this.type = BucketOutputType.PORT;
42 + this.outPort = checkNotNull(outPort);
43 + this.outGroup = null;
44 + }
45 +
46 + protected GroupBucketIdentifier(int label,
47 + GroupKey outGroup) {
48 + this.label = label;
49 + this.type = BucketOutputType.GROUP;
50 + this.outPort = null;
51 + this.outGroup = checkNotNull(outGroup);
52 + }
53 +
54 + protected int label() {
55 + return this.label;
56 + }
57 +
58 + protected BucketOutputType type() {
59 + return this.type;
60 + }
61 +
62 + protected PortNumber outPort() {
63 + return this.outPort;
64 + }
65 +
66 + protected GroupKey outGroup() {
67 + return this.outGroup;
68 + }
69 +}
70 +
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.grouphandler;
18 +
19 +import static com.google.common.base.Preconditions.checkNotNull;
20 +
21 +import java.util.HashSet;
22 +import java.util.Objects;
23 +import java.util.Set;
24 +
25 +import org.onosproject.net.DeviceId;
26 +import org.onosproject.net.group.GroupKey;
27 +
28 +/**
29 + * Representation of a set of neighbor switch dpids along with edge node
30 + * label. Meant to be used as a lookup-key in a hash-map to retrieve an
31 + * ECMP-group that hashes packets to a set of ports connecting to the
32 + * neighbors in this set.
33 + */
34 +public class NeighborSet implements GroupKey {
35 + private final Set<DeviceId> neighbors;
36 + private final int edgeLabel;
37 +
38 + /**
39 + * Constructor with set of neighbors. Edge label is
40 + * default to -1.
41 + *
42 + * @param neighbors set of neighbors to be part of neighbor set
43 + */
44 + public NeighborSet(Set<DeviceId> neighbors) {
45 + checkNotNull(neighbors);
46 + this.edgeLabel = -1;
47 + this.neighbors = new HashSet<DeviceId>();
48 + this.neighbors.addAll(neighbors);
49 + }
50 +
51 + /**
52 + * Constructor with set of neighbors and edge label.
53 + *
54 + * @param neighbors set of neighbors to be part of neighbor set
55 + * @param edgeLabel label to be pushed as part of group operation
56 + */
57 + public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
58 + checkNotNull(neighbors);
59 + this.edgeLabel = edgeLabel;
60 + this.neighbors = new HashSet<DeviceId>();
61 + this.neighbors.addAll(neighbors);
62 + }
63 +
64 + /**
65 + * Default constructor for kryo serialization.
66 + */
67 + public NeighborSet() {
68 + this.edgeLabel = -1;
69 + this.neighbors = new HashSet<DeviceId>();
70 + }
71 +
72 + /**
73 + * Gets the neighbors part of neighbor set.
74 + *
75 + * @return set of neighbor identifiers
76 + */
77 + public Set<DeviceId> getDeviceIds() {
78 + return neighbors;
79 + }
80 +
81 + /**
82 + * Gets the label associated with neighbor set.
83 + *
84 + * @return integer
85 + */
86 + public int getEdgeLabel() {
87 + return edgeLabel;
88 + }
89 +
90 + // The list of neighbor ids and label are used for comparison.
91 + @Override
92 + public boolean equals(Object o) {
93 + if (this == o) {
94 + return true;
95 + }
96 + if (!(o instanceof NeighborSet)) {
97 + return false;
98 + }
99 + NeighborSet that = (NeighborSet) o;
100 + return (this.neighbors.containsAll(that.neighbors) &&
101 + that.neighbors.containsAll(this.neighbors) &&
102 + (this.edgeLabel == that.edgeLabel));
103 + }
104 +
105 + // The list of neighbor ids and label are used for comparison.
106 + @Override
107 + public int hashCode() {
108 + int result = 17;
109 + int combinedHash = 0;
110 + for (DeviceId d : neighbors) {
111 + combinedHash = combinedHash + Objects.hash(d);
112 + }
113 + result = 31 * result + combinedHash + Objects.hash(edgeLabel);
114 +
115 + return result;
116 + }
117 +
118 + @Override
119 + public String toString() {
120 + return " Neighborset Sw: " + neighbors
121 + + " and Label: " + edgeLabel;
122 + }
123 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import static org.slf4j.LoggerFactory.getLogger;
19 +
20 +import java.util.ArrayList;
21 +import java.util.Arrays;
22 +import java.util.HashMap;
23 +import java.util.Iterator;
24 +import java.util.List;
25 +
26 +import org.onosproject.core.ApplicationId;
27 +import org.onosproject.core.GroupId;
28 +import org.onosproject.grouphandler.GroupBucketIdentifier.BucketOutputType;
29 +import org.onosproject.net.DeviceId;
30 +import org.onosproject.net.PortNumber;
31 +import org.onosproject.net.flow.DefaultTrafficTreatment;
32 +import org.onosproject.net.flow.TrafficTreatment;
33 +import org.onosproject.net.group.DefaultGroupBucket;
34 +import org.onosproject.net.group.DefaultGroupDescription;
35 +import org.onosproject.net.group.GroupBucket;
36 +import org.onosproject.net.group.GroupBuckets;
37 +import org.onosproject.net.group.GroupDescription;
38 +import org.onosproject.net.group.GroupEvent;
39 +import org.onosproject.net.group.GroupKey;
40 +import org.onosproject.net.group.GroupService;
41 +import org.onosproject.net.link.LinkService;
42 +import org.slf4j.Logger;
43 +
44 +/**
45 + * A module to create group chains based on the specified device
46 + * ports and label stack to be applied on each port.
47 + */
48 +public class PolicyGroupHandler extends DefaultGroupHandler {
49 +
50 + private final Logger log = getLogger(getClass());
51 + private HashMap<GroupKey, GroupKey> dependentGroups =
52 + new HashMap<GroupKey, GroupKey>();
53 +
54 + /**
55 + * Creates policy group handler object.
56 + *
57 + * @param deviceId device identifier
58 + * @param appId application identifier
59 + * @param config interface to retrieve the device properties
60 + * @param linkService link service object
61 + * @param groupService group service object
62 + * @return policy group handler type
63 + */
64 + public PolicyGroupHandler(DeviceId deviceId,
65 + ApplicationId appId,
66 + DeviceProperties config,
67 + LinkService linkService,
68 + GroupService groupService) {
69 + super(deviceId, appId, config, linkService, groupService);
70 + }
71 +
72 + public PolicyGroupIdentifier createPolicyGroupChain(String id,
73 + List<PolicyGroupParams> params) {
74 + List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
75 + for (PolicyGroupParams param: params) {
76 + List<PortNumber> ports = param.getPorts();
77 + if (ports == null) {
78 + log.warn("createPolicyGroupChain in sw {} with wrong "
79 + + "input parameters", deviceId);
80 + return null;
81 + }
82 +
83 + int labelStackSize = (param.getLabelStack() != null) ?
84 + param.getLabelStack().size() : 0;
85 +
86 + if (labelStackSize > 1) {
87 + for (PortNumber sp : ports) {
88 + PolicyGroupIdentifier previousGroupkey = null;
89 + DeviceId neighbor = portDeviceMap.get(sp);
90 + for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
91 + int label = param.getLabelStack().get(idx).intValue();
92 + if (idx == (labelStackSize - 1)) {
93 + // Innermost Group
94 + GroupBucketIdentifier bucketId =
95 + new GroupBucketIdentifier(label,
96 + previousGroupkey);
97 + bucketIds.add(bucketId);
98 + } else if (idx == 0) {
99 + // Outermost Group
100 + List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
101 + GroupBucketIdentifier bucketId =
102 + new GroupBucketIdentifier(label, sp);
103 + PolicyGroupIdentifier key = new
104 + PolicyGroupIdentifier(id,
105 + Arrays.asList(param),
106 + Arrays.asList(bucketId));
107 + TrafficTreatment.Builder tBuilder =
108 + DefaultTrafficTreatment.builder();
109 + tBuilder.setOutput(sp)
110 + .setEthDst(deviceConfig.
111 + getDeviceMac(neighbor))
112 + .setEthSrc(nodeMacAddr)
113 + .pushMpls()
114 + .setMpls(label);
115 + outBuckets.add(DefaultGroupBucket.
116 + createSelectGroupBucket(tBuilder.build()));
117 + GroupDescription desc = new
118 + DefaultGroupDescription(deviceId,
119 + GroupDescription.Type.INDIRECT,
120 + new GroupBuckets(outBuckets));
121 + //TODO: BoS
122 + previousGroupkey = key;
123 + groupService.addGroup(desc);
124 + } else {
125 + // Intermediate Groups
126 + GroupBucketIdentifier bucketId =
127 + new GroupBucketIdentifier(label,
128 + previousGroupkey);
129 + PolicyGroupIdentifier key = new
130 + PolicyGroupIdentifier(id,
131 + Arrays.asList(param),
132 + Arrays.asList(bucketId));
133 + // Add to group dependency list
134 + dependentGroups.put(previousGroupkey, key);
135 + previousGroupkey = key;
136 + }
137 + }
138 + }
139 + } else {
140 + int label = -1;
141 + if (labelStackSize == 1) {
142 + label = param.getLabelStack().get(0).intValue();
143 + }
144 + for (PortNumber sp : ports) {
145 + GroupBucketIdentifier bucketId =
146 + new GroupBucketIdentifier(label, sp);
147 + bucketIds.add(bucketId);
148 + }
149 + }
150 + }
151 + PolicyGroupIdentifier innermostGroupkey = null;
152 + if (!bucketIds.isEmpty()) {
153 + innermostGroupkey = new
154 + PolicyGroupIdentifier(id,
155 + params,
156 + bucketIds);
157 + // Add to group dependency list
158 + boolean fullyResolved = true;
159 + for (GroupBucketIdentifier bucketId:bucketIds) {
160 + if (bucketId.type() == BucketOutputType.GROUP) {
161 + dependentGroups.put(bucketId.outGroup(),
162 + innermostGroupkey);
163 + fullyResolved = false;
164 + }
165 + }
166 +
167 + if (fullyResolved) {
168 + List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
169 + for (GroupBucketIdentifier bucketId:bucketIds) {
170 + DeviceId neighbor = portDeviceMap.
171 + get(bucketId.outPort());
172 + TrafficTreatment.Builder tBuilder =
173 + DefaultTrafficTreatment.builder();
174 + tBuilder.setOutput(bucketId.outPort())
175 + .setEthDst(deviceConfig.
176 + getDeviceMac(neighbor))
177 + .setEthSrc(nodeMacAddr)
178 + .pushMpls()
179 + .setMpls(bucketId.label());
180 + //TODO: BoS
181 + outBuckets.add(DefaultGroupBucket.
182 + createSelectGroupBucket(tBuilder.build()));
183 + }
184 + GroupDescription desc = new
185 + DefaultGroupDescription(deviceId,
186 + GroupDescription.Type.SELECT,
187 + new GroupBuckets(outBuckets));
188 + groupService.addGroup(desc);
189 + }
190 + }
191 + return innermostGroupkey;
192 + }
193 +
194 + @Override
195 + protected void handleGroupEvent(GroupEvent event) {
196 + if (event.type() == GroupEvent.Type.GROUP_ADDED) {
197 + if (dependentGroups.get(event.subject().appCookie()) != null) {
198 + PolicyGroupIdentifier dependentGroupKey = (PolicyGroupIdentifier)
199 + dependentGroups.get(event.subject().appCookie());
200 + dependentGroups.remove(event.subject().appCookie());
201 + boolean fullyResolved = true;
202 + for (GroupBucketIdentifier bucketId:
203 + dependentGroupKey.bucketIds()) {
204 + if (bucketId.type() != BucketOutputType.GROUP) {
205 + continue;
206 + }
207 + if (dependentGroups.containsKey(bucketId.outGroup())) {
208 + fullyResolved = false;
209 + break;
210 + }
211 + }
212 +
213 + if (fullyResolved) {
214 + List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
215 + for (GroupBucketIdentifier bucketId:
216 + dependentGroupKey.bucketIds()) {
217 + TrafficTreatment.Builder tBuilder =
218 + DefaultTrafficTreatment.builder();
219 + tBuilder.pushMpls()
220 + .setMpls(bucketId.label());
221 + //TODO: BoS
222 + if (bucketId.type() == BucketOutputType.PORT) {
223 + DeviceId neighbor = portDeviceMap.
224 + get(bucketId.outPort());
225 + tBuilder.setOutput(bucketId.outPort())
226 + .setEthDst(deviceConfig.
227 + getDeviceMac(neighbor))
228 + .setEthSrc(nodeMacAddr);
229 + } else {
230 + if (groupService.
231 + getGroup(deviceId,
232 + bucketId.outGroup()) == null) {
233 + throw new IllegalStateException();
234 + }
235 + GroupId indirectGroupId = groupService.
236 + getGroup(deviceId,
237 + bucketId.outGroup()).id();
238 + tBuilder.group(indirectGroupId);
239 + }
240 + outBuckets.add(DefaultGroupBucket.
241 + createSelectGroupBucket(tBuilder.build()));
242 + }
243 + GroupDescription desc = new
244 + DefaultGroupDescription(deviceId,
245 + GroupDescription.Type.SELECT,
246 + new GroupBuckets(outBuckets));
247 + groupService.addGroup(desc);
248 + }
249 + }
250 + }
251 + }
252 +
253 + public GroupKey generatePolicyGroupKey(String id,
254 + List<PolicyGroupParams> params) {
255 + List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
256 + for (PolicyGroupParams param: params) {
257 + List<PortNumber> ports = param.getPorts();
258 + if (ports == null) {
259 + log.warn("generateGroupKey in sw {} with wrong "
260 + + "input parameters", deviceId);
261 + return null;
262 + }
263 +
264 + int labelStackSize = (param.getLabelStack() != null)
265 + ? param.getLabelStack().size() : 0;
266 +
267 + if (labelStackSize > 1) {
268 + for (PortNumber sp : ports) {
269 + PolicyGroupIdentifier previousGroupkey = null;
270 + for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
271 + int label = param.getLabelStack().get(idx).intValue();
272 + if (idx == (labelStackSize - 1)) {
273 + // Innermost Group
274 + GroupBucketIdentifier bucketId =
275 + new GroupBucketIdentifier(label,
276 + previousGroupkey);
277 + bucketIds.add(bucketId);
278 + } else if (idx == 0) {
279 + // Outermost Group
280 + GroupBucketIdentifier bucketId =
281 + new GroupBucketIdentifier(label, sp);
282 + PolicyGroupIdentifier key = new
283 + PolicyGroupIdentifier(id,
284 + Arrays.asList(param),
285 + Arrays.asList(bucketId));
286 + previousGroupkey = key;
287 + } else {
288 + // Intermediate Groups
289 + GroupBucketIdentifier bucketId =
290 + new GroupBucketIdentifier(label,
291 + previousGroupkey);
292 + PolicyGroupIdentifier key = new
293 + PolicyGroupIdentifier(id,
294 + Arrays.asList(param),
295 + Arrays.asList(bucketId));
296 + previousGroupkey = key;
297 + }
298 + }
299 + }
300 + } else {
301 + int label = -1;
302 + if (labelStackSize == 1) {
303 + label = param.getLabelStack().get(0).intValue();
304 + }
305 + for (PortNumber sp : ports) {
306 + GroupBucketIdentifier bucketId =
307 + new GroupBucketIdentifier(label, sp);
308 + bucketIds.add(bucketId);
309 + }
310 + }
311 + }
312 + PolicyGroupIdentifier innermostGroupkey = null;
313 + if (!bucketIds.isEmpty()) {
314 + innermostGroupkey = new
315 + PolicyGroupIdentifier(id,
316 + params,
317 + bucketIds);
318 + }
319 + return innermostGroupkey;
320 + }
321 +
322 + public void removeGroupChain(GroupKey key) {
323 + if (!(key instanceof PolicyGroupIdentifier)) {
324 + throw new IllegalArgumentException();
325 + }
326 + List<GroupKey> groupsToBeDeleted = new ArrayList<GroupKey>();
327 + groupsToBeDeleted.add(key);
328 +
329 + Iterator<GroupKey> it = groupsToBeDeleted.iterator();
330 +
331 + while (it.hasNext()) {
332 + PolicyGroupIdentifier innerMostGroupKey =
333 + (PolicyGroupIdentifier) it.next();
334 + for (GroupBucketIdentifier bucketId:
335 + innerMostGroupKey.bucketIds()) {
336 + if (bucketId.type() != BucketOutputType.GROUP) {
337 + groupsToBeDeleted.add(bucketId.outGroup());
338 + }
339 + }
340 + groupService.removeGroup(deviceId, innerMostGroupKey, appId);
341 + it.remove();
342 + }
343 + }
344 +
345 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import java.util.List;
19 +
20 +import org.onosproject.net.group.GroupKey;
21 +
22 +/**
23 + * Representation of policy based group identifiers.
24 + * Opaque to group handler applications and only the outermost
25 + * policy group identifier in a chain is visible to the applications.
26 + */
27 +public class PolicyGroupIdentifier implements GroupKey {
28 + private String id;
29 + private List<PolicyGroupParams> inputParams;
30 + private List<GroupBucketIdentifier> bucketIds;
31 +
32 + /**
33 + * Constructor.
34 + *
35 + * @param id unique identifier associated with the policy group
36 + * @param input policy group params associated with this group
37 + * @param bucketIds buckets associated with this group
38 + */
39 + protected PolicyGroupIdentifier(String id,
40 + List<PolicyGroupParams> input,
41 + List<GroupBucketIdentifier> bucketIds) {
42 + this.id = id;
43 + this.inputParams = input;
44 + this.bucketIds = bucketIds;
45 + }
46 +
47 + /**
48 + * Returns the bucket identifier list associated with the policy
49 + * group identifier.
50 + *
51 + * @return list of bucket identifier
52 + */
53 + protected List<GroupBucketIdentifier> bucketIds() {
54 + return this.bucketIds;
55 + }
56 +
57 + @Override
58 + public int hashCode() {
59 + int result = 17;
60 + int combinedHash = 0;
61 + for (PolicyGroupParams input:inputParams) {
62 + combinedHash = combinedHash + input.hashCode();
63 + }
64 + for (GroupBucketIdentifier bucketId:bucketIds) {
65 + combinedHash = combinedHash + bucketId.hashCode();
66 + }
67 + result = 31 * result + combinedHash;
68 +
69 + return result;
70 + }
71 +
72 + @Override
73 + public boolean equals(Object obj) {
74 + if (this == obj) {
75 + return true;
76 + }
77 +
78 + if (obj instanceof PolicyGroupIdentifier) {
79 + PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
80 + boolean result = this.id.equals(that.id);
81 + result = result &&
82 + this.inputParams.containsAll(that.inputParams) &&
83 + that.inputParams.containsAll(this.inputParams);
84 + result = result &&
85 + this.bucketIds.containsAll(that.bucketIds) &&
86 + that.bucketIds.containsAll(this.bucketIds);
87 + return result;
88 + }
89 +
90 + return false;
91 + }
92 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.grouphandler;
17 +
18 +import static com.google.common.base.Preconditions.checkNotNull;
19 +
20 +import java.util.List;
21 +import java.util.Objects;
22 +
23 +import org.onosproject.net.PortNumber;
24 +
25 +/**
26 + * Representation of parameters used to create policy based groups.
27 + */
28 +public class PolicyGroupParams {
29 + private final List<PortNumber> ports;
30 + private final List<Integer> labelStack;
31 +
32 + /**
33 + * Constructor.
34 + *
35 + * @param labelStack mpls label stack to be applied on the ports
36 + * @param ports ports to be part of the policy group
37 + */
38 + public PolicyGroupParams(List<Integer> labelStack,
39 + List<PortNumber> ports) {
40 + this.ports = checkNotNull(ports);
41 + this.labelStack = checkNotNull(labelStack);
42 + }
43 +
44 + /**
45 + * Returns the ports associated with the policy group params.
46 + *
47 + * @return list of port numbers
48 + */
49 + public List<PortNumber> getPorts() {
50 + return ports;
51 + }
52 +
53 + /**
54 + * Returns the label stack associated with the policy group params.
55 + *
56 + * @return list of integers
57 + */
58 + public List<Integer> getLabelStack() {
59 + return labelStack;
60 + }
61 +
62 + @Override
63 + public int hashCode() {
64 + int result = 17;
65 + int combinedHash = 0;
66 + for (PortNumber port:ports) {
67 + combinedHash = combinedHash + port.hashCode();
68 + }
69 + combinedHash = combinedHash + Objects.hash(labelStack);
70 + result = 31 * result + combinedHash;
71 +
72 + return result;
73 + }
74 +
75 + @Override
76 + public boolean equals(Object obj) {
77 + if (this == obj) {
78 + return true;
79 + }
80 +
81 + if (obj instanceof PolicyGroupParams) {
82 + PolicyGroupParams that = (PolicyGroupParams) obj;
83 + boolean result = this.labelStack.equals(that.labelStack);
84 + result = result &&
85 + this.ports.containsAll(that.ports) &&
86 + that.ports.containsAll(this.ports);
87 + return result;
88 + }
89 +
90 + return false;
91 + }
92 +}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.net.group; 16 package org.onosproject.net.group;
17 17
18 +import static com.google.common.base.MoreObjects.toStringHelper;
18 import static org.slf4j.LoggerFactory.getLogger; 19 import static org.slf4j.LoggerFactory.getLogger;
19 20
20 import java.util.Objects; 21 import java.util.Objects;
...@@ -205,4 +206,13 @@ public class DefaultGroup extends DefaultGroupDescription ...@@ -205,4 +206,13 @@ public class DefaultGroup extends DefaultGroupDescription
205 } 206 }
206 return false; 207 return false;
207 } 208 }
209 +
210 + @Override
211 + public String toString() {
212 + return toStringHelper(this)
213 + .add("description", super.toString())
214 + .add("groupid", id)
215 + .add("state", state)
216 + .toString();
217 + }
208 } 218 }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.net.group; 16 package org.onosproject.net.group;
17 17
18 +import static com.google.common.base.MoreObjects.toStringHelper;
18 import static com.google.common.base.Preconditions.checkArgument; 19 import static com.google.common.base.Preconditions.checkArgument;
19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.base.Preconditions.checkNotNull;
20 21
...@@ -212,4 +213,11 @@ public final class DefaultGroupBucket implements GroupBucket { ...@@ -212,4 +213,11 @@ public final class DefaultGroupBucket implements GroupBucket {
212 return false; 213 return false;
213 } 214 }
214 215
216 + @Override
217 + public String toString() {
218 + return toStringHelper(this)
219 + .add("type", type)
220 + .add("treatment", treatment)
221 + .toString();
222 + }
215 } 223 }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.net.group; 16 package org.onosproject.net.group;
17 17
18 +import static com.google.common.base.MoreObjects.toStringHelper;
18 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.base.Preconditions.checkNotNull;
19 20
20 import java.util.Objects; 21 import java.util.Objects;
...@@ -168,4 +169,13 @@ public class DefaultGroupDescription implements GroupDescription { ...@@ -168,4 +169,13 @@ public class DefaultGroupDescription implements GroupDescription {
168 return false; 169 return false;
169 } 170 }
170 171
172 + @Override
173 + public String toString() {
174 + return toStringHelper(this)
175 + .add("deviceId", deviceId)
176 + .add("type", type)
177 + .add("buckets", buckets)
178 + .add("appId", appId)
179 + .toString();
180 + }
171 } 181 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.net.group; 16 package org.onosproject.net.group;
17 17
18 +import static com.google.common.base.MoreObjects.toStringHelper;
18 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.base.Preconditions.checkNotNull;
19 20
20 import java.util.List; 21 import java.util.List;
...@@ -66,4 +67,10 @@ public final class GroupBuckets { ...@@ -66,4 +67,10 @@ public final class GroupBuckets {
66 return false; 67 return false;
67 } 68 }
68 69
70 + @Override
71 + public String toString() {
72 + return toStringHelper(this)
73 + .add("buckets", buckets)
74 + .toString();
75 + }
69 } 76 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -132,8 +132,9 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> { ...@@ -132,8 +132,9 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
132 * Indicates the first group audit is completed. 132 * Indicates the first group audit is completed.
133 * 133 *
134 * @param deviceId the device ID 134 * @param deviceId the device ID
135 + * @param completed initial audit status
135 */ 136 */
136 - void deviceInitialAuditCompleted(DeviceId deviceId); 137 + void deviceInitialAuditCompleted(DeviceId deviceId, boolean completed);
137 138
138 /** 139 /**
139 * Retrieves the initial group audit status for a device. 140 * Retrieves the initial group audit status for a device.
......
...@@ -32,6 +32,9 @@ import org.onosproject.core.ApplicationId; ...@@ -32,6 +32,9 @@ import org.onosproject.core.ApplicationId;
32 import org.onosproject.event.AbstractListenerRegistry; 32 import org.onosproject.event.AbstractListenerRegistry;
33 import org.onosproject.event.EventDeliveryService; 33 import org.onosproject.event.EventDeliveryService;
34 import org.onosproject.net.DeviceId; 34 import org.onosproject.net.DeviceId;
35 +import org.onosproject.net.device.DeviceEvent;
36 +import org.onosproject.net.device.DeviceListener;
37 +import org.onosproject.net.device.DeviceService;
35 import org.onosproject.net.group.Group; 38 import org.onosproject.net.group.Group;
36 import org.onosproject.net.group.GroupBuckets; 39 import org.onosproject.net.group.GroupBuckets;
37 import org.onosproject.net.group.GroupDescription; 40 import org.onosproject.net.group.GroupDescription;
...@@ -67,17 +70,22 @@ public class GroupManager ...@@ -67,17 +70,22 @@ public class GroupManager
67 private final AbstractListenerRegistry<GroupEvent, GroupListener> 70 private final AbstractListenerRegistry<GroupEvent, GroupListener>
68 listenerRegistry = new AbstractListenerRegistry<>(); 71 listenerRegistry = new AbstractListenerRegistry<>();
69 private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate(); 72 private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
73 + private final DeviceListener deviceListener = new InternalDeviceListener();
70 74
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected GroupStore store; 76 protected GroupStore store;
73 77
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 + protected DeviceService deviceService;
80 +
81 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected EventDeliveryService eventDispatcher; 82 protected EventDeliveryService eventDispatcher;
76 83
77 @Activate 84 @Activate
78 public void activate() { 85 public void activate() {
79 store.setDelegate(delegate); 86 store.setDelegate(delegate);
80 eventDispatcher.addSink(GroupEvent.class, listenerRegistry); 87 eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
88 + deviceService.addListener(deviceListener);
81 log.info("Started"); 89 log.info("Started");
82 } 90 }
83 91
...@@ -232,6 +240,8 @@ public class GroupManager ...@@ -232,6 +240,8 @@ public class GroupManager
232 GroupOperations groupOps = null; 240 GroupOperations groupOps = null;
233 switch (event.type()) { 241 switch (event.type()) {
234 case GROUP_ADD_REQUESTED: 242 case GROUP_ADD_REQUESTED:
243 + log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
244 + group.id(), group.deviceId());
235 GroupOperation groupAddOp = GroupOperation. 245 GroupOperation groupAddOp = GroupOperation.
236 createAddGroupOperation(group.id(), 246 createAddGroupOperation(group.id(),
237 group.type(), 247 group.type(),
...@@ -242,6 +252,8 @@ public class GroupManager ...@@ -242,6 +252,8 @@ public class GroupManager
242 break; 252 break;
243 253
244 case GROUP_UPDATE_REQUESTED: 254 case GROUP_UPDATE_REQUESTED:
255 + log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
256 + group.id(), group.deviceId());
245 GroupOperation groupModifyOp = GroupOperation. 257 GroupOperation groupModifyOp = GroupOperation.
246 createModifyGroupOperation(group.id(), 258 createModifyGroupOperation(group.id(),
247 group.type(), 259 group.type(),
...@@ -252,6 +264,8 @@ public class GroupManager ...@@ -252,6 +264,8 @@ public class GroupManager
252 break; 264 break;
253 265
254 case GROUP_REMOVE_REQUESTED: 266 case GROUP_REMOVE_REQUESTED:
267 + log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
268 + group.id(), group.deviceId());
255 GroupOperation groupDeleteOp = GroupOperation. 269 GroupOperation groupDeleteOp = GroupOperation.
256 createDeleteGroupOperation(group.id(), 270 createDeleteGroupOperation(group.id(),
257 group.type()); 271 group.type());
...@@ -294,10 +308,14 @@ public class GroupManager ...@@ -294,10 +308,14 @@ public class GroupManager
294 GroupProvider gp = getProvider(group.deviceId()); 308 GroupProvider gp = getProvider(group.deviceId());
295 switch (group.state()) { 309 switch (group.state()) {
296 case PENDING_DELETE: 310 case PENDING_DELETE:
311 + log.debug("Group {} delete confirmation from device {}",
312 + group, group.deviceId());
297 store.removeGroupEntry(group); 313 store.removeGroupEntry(group);
298 break; 314 break;
299 case ADDED: 315 case ADDED:
300 case PENDING_ADD: 316 case PENDING_ADD:
317 + log.debug("Group {} is in store but not on device {}",
318 + group, group.deviceId());
301 GroupOperation groupAddOp = GroupOperation. 319 GroupOperation groupAddOp = GroupOperation.
302 createAddGroupOperation(group.id(), 320 createAddGroupOperation(group.id(),
303 group.type(), 321 group.type(),
...@@ -314,7 +332,8 @@ public class GroupManager ...@@ -314,7 +332,8 @@ public class GroupManager
314 332
315 333
316 private void extraneousGroup(Group group) { 334 private void extraneousGroup(Group group) {
317 - log.debug("Group {} is on switch but not in store.", group); 335 + log.debug("Group {} is on device {} but not in store.",
336 + group, group.deviceId());
318 checkValidity(); 337 checkValidity();
319 store.addOrUpdateExtraneousGroupEntry(group); 338 store.addOrUpdateExtraneousGroupEntry(group);
320 } 339 }
...@@ -322,13 +341,16 @@ public class GroupManager ...@@ -322,13 +341,16 @@ public class GroupManager
322 private void groupAdded(Group group) { 341 private void groupAdded(Group group) {
323 checkValidity(); 342 checkValidity();
324 343
325 - log.trace("Group {}", group); 344 + log.trace("Group {} Added or Updated in device {}",
345 + group, group.deviceId());
326 store.addOrUpdateGroupEntry(group); 346 store.addOrUpdateGroupEntry(group);
327 } 347 }
328 348
329 @Override 349 @Override
330 public void pushGroupMetrics(DeviceId deviceId, 350 public void pushGroupMetrics(DeviceId deviceId,
331 Collection<Group> groupEntries) { 351 Collection<Group> groupEntries) {
352 + log.trace("Received group metrics from device {}",
353 + deviceId);
332 boolean deviceInitialAuditStatus = 354 boolean deviceInitialAuditStatus =
333 store.deviceInitialAuditStatus(deviceId); 355 store.deviceInitialAuditStatus(deviceId);
334 Set<Group> southboundGroupEntries = 356 Set<Group> southboundGroupEntries =
...@@ -338,31 +360,75 @@ public class GroupManager ...@@ -338,31 +360,75 @@ public class GroupManager
338 Set<Group> extraneousStoredEntries = 360 Set<Group> extraneousStoredEntries =
339 Sets.newHashSet(store.getExtraneousGroups(deviceId)); 361 Sets.newHashSet(store.getExtraneousGroups(deviceId));
340 362
363 + log.trace("Displaying all southboundGroupEntries for device {}", deviceId);
364 + for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
365 + Group group = it.next();
366 + log.trace("Group {} in device {}", group, deviceId);
367 + }
368 +
369 + log.trace("Displaying all stored group entries for device {}", deviceId);
370 + for (Iterator<Group> it = storedGroupEntries.iterator(); it.hasNext();) {
371 + Group group = it.next();
372 + log.trace("Stored Group {} for device {}", group, deviceId);
373 + }
374 +
341 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) { 375 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
342 Group group = it.next(); 376 Group group = it.next();
343 if (storedGroupEntries.remove(group)) { 377 if (storedGroupEntries.remove(group)) {
344 // we both have the group, let's update some info then. 378 // we both have the group, let's update some info then.
379 + log.trace("Group AUDIT: group {} exists "
380 + + "in both planes for device {}",
381 + group.id(), deviceId);
345 groupAdded(group); 382 groupAdded(group);
346 it.remove(); 383 it.remove();
347 } 384 }
348 } 385 }
349 for (Group group : southboundGroupEntries) { 386 for (Group group : southboundGroupEntries) {
350 // there are groups in the switch that aren't in the store 387 // there are groups in the switch that aren't in the store
388 + log.trace("Group AUDIT: extraneous group {} exists "
389 + + "in data plane for device {}",
390 + group.id(), deviceId);
351 extraneousStoredEntries.remove(group); 391 extraneousStoredEntries.remove(group);
352 extraneousGroup(group); 392 extraneousGroup(group);
353 } 393 }
354 for (Group group : storedGroupEntries) { 394 for (Group group : storedGroupEntries) {
355 // there are groups in the store that aren't in the switch 395 // there are groups in the store that aren't in the switch
396 + log.trace("Group AUDIT: group {} missing "
397 + + "in data plane for device {}",
398 + group.id(), deviceId);
356 groupMissing(group); 399 groupMissing(group);
357 } 400 }
358 for (Group group : extraneousStoredEntries) { 401 for (Group group : extraneousStoredEntries) {
359 // there are groups in the extraneous store that 402 // there are groups in the extraneous store that
360 // aren't in the switch 403 // aren't in the switch
404 + log.trace("Group AUDIT: clearing extransoeus group {} "
405 + + "from store for device {}",
406 + group.id(), deviceId);
361 store.removeExtraneousGroupEntry(group); 407 store.removeExtraneousGroupEntry(group);
362 } 408 }
363 409
364 if (!deviceInitialAuditStatus) { 410 if (!deviceInitialAuditStatus) {
365 - store.deviceInitialAuditCompleted(deviceId); 411 + log.debug("Group AUDIT: Setting device {} initial "
412 + + "AUDIT completed", deviceId);
413 + store.deviceInitialAuditCompleted(deviceId, true);
414 + }
415 + }
416 + }
417 +
418 + private class InternalDeviceListener implements DeviceListener {
419 +
420 + @Override
421 + public void event(DeviceEvent event) {
422 + switch (event.type()) {
423 + case DEVICE_REMOVED:
424 + log.debug("Clearing device {} initial "
425 + + "AUDIT completed status as device is going down",
426 + event.subject().id());
427 + store.deviceInitialAuditCompleted(event.subject().id(), false);
428 + break;
429 +
430 + default:
431 + break;
366 } 432 }
367 } 433 }
368 } 434 }
......
...@@ -32,6 +32,7 @@ import org.onosproject.core.GroupId; ...@@ -32,6 +32,7 @@ import org.onosproject.core.GroupId;
32 import org.onosproject.event.impl.TestEventDispatcher; 32 import org.onosproject.event.impl.TestEventDispatcher;
33 import org.onosproject.net.DeviceId; 33 import org.onosproject.net.DeviceId;
34 import org.onosproject.net.PortNumber; 34 import org.onosproject.net.PortNumber;
35 +import org.onosproject.net.device.impl.DeviceManager;
35 import org.onosproject.net.flow.DefaultTrafficTreatment; 36 import org.onosproject.net.flow.DefaultTrafficTreatment;
36 import org.onosproject.net.flow.TrafficTreatment; 37 import org.onosproject.net.flow.TrafficTreatment;
37 import org.onosproject.net.group.DefaultGroup; 38 import org.onosproject.net.group.DefaultGroup;
...@@ -81,6 +82,7 @@ public class GroupManagerTest { ...@@ -81,6 +82,7 @@ public class GroupManagerTest {
81 public void setUp() { 82 public void setUp() {
82 mgr = new GroupManager(); 83 mgr = new GroupManager();
83 groupService = mgr; 84 groupService = mgr;
85 + mgr.deviceService = new DeviceManager();
84 mgr.store = new SimpleGroupStore(); 86 mgr.store = new SimpleGroupStore();
85 mgr.eventDispatcher = new TestEventDispatcher(); 87 mgr.eventDispatcher = new TestEventDispatcher();
86 providerRegistry = mgr; 88 providerRegistry = mgr;
...@@ -147,11 +149,34 @@ public class GroupManagerTest { ...@@ -147,11 +149,34 @@ public class GroupManagerTest {
147 */ 149 */
148 @Test 150 @Test
149 public void testGroupService() { 151 public void testGroupService() {
152 + // Test Group creation before AUDIT process
153 + testGroupCreationBeforeAudit();
154 +
155 + // Test initial group audit process
156 + testInitialAuditWithPendingGroupRequests();
157 +
158 + // Test audit with extraneous and missing groups
159 + testAuditWithExtraneousMissingGroups();
160 +
161 + // Test audit with confirmed groups
162 + testAuditWithConfirmedGroups();
163 +
164 + // Test group add bucket operations
165 + testAddBuckets();
166 +
167 + // Test group remove bucket operations
168 + testRemoveBuckets();
169 +
170 + // Test group remove operations
171 + testRemoveGroup();
172 + }
173 +
174 + // Test Group creation before AUDIT process
175 + private void testGroupCreationBeforeAudit() {
150 PortNumber[] ports1 = {PortNumber.portNumber(31), 176 PortNumber[] ports1 = {PortNumber.portNumber(31),
151 PortNumber.portNumber(32)}; 177 PortNumber.portNumber(32)};
152 PortNumber[] ports2 = {PortNumber.portNumber(41), 178 PortNumber[] ports2 = {PortNumber.portNumber(41),
153 PortNumber.portNumber(42)}; 179 PortNumber.portNumber(42)};
154 - // Test Group creation before AUDIT process
155 TestGroupKey key = new TestGroupKey("group1BeforeAudit"); 180 TestGroupKey key = new TestGroupKey("group1BeforeAudit");
156 List<GroupBucket> buckets = new ArrayList<GroupBucket>(); 181 List<GroupBucket> buckets = new ArrayList<GroupBucket>();
157 List<PortNumber> outPorts = new ArrayList<PortNumber>(); 182 List<PortNumber> outPorts = new ArrayList<PortNumber>();
...@@ -177,8 +202,14 @@ public class GroupManagerTest { ...@@ -177,8 +202,14 @@ public class GroupManagerTest {
177 internalProvider.validate(DID, null); 202 internalProvider.validate(DID, null);
178 assertEquals(null, groupService.getGroup(DID, key)); 203 assertEquals(null, groupService.getGroup(DID, key));
179 assertEquals(0, Iterables.size(groupService.getGroups(DID, appId))); 204 assertEquals(0, Iterables.size(groupService.getGroups(DID, appId)));
205 + }
180 206
181 - // Test initial group audit process 207 + // Test initial AUDIT process with pending group requests
208 + private void testInitialAuditWithPendingGroupRequests() {
209 + PortNumber[] ports1 = {PortNumber.portNumber(31),
210 + PortNumber.portNumber(32)};
211 + PortNumber[] ports2 = {PortNumber.portNumber(41),
212 + PortNumber.portNumber(42)};
182 GroupId gId1 = new DefaultGroupId(1); 213 GroupId gId1 = new DefaultGroupId(1);
183 Group group1 = createSouthboundGroupEntry(gId1, 214 Group group1 = createSouthboundGroupEntry(gId1,
184 Arrays.asList(ports1), 215 Arrays.asList(ports1),
...@@ -193,50 +224,76 @@ public class GroupManagerTest { ...@@ -193,50 +224,76 @@ public class GroupManagerTest {
193 providerService.pushGroupMetrics(DID, groupEntries); 224 providerService.pushGroupMetrics(DID, groupEntries);
194 // First group metrics would trigger the device audit completion 225 // First group metrics would trigger the device audit completion
195 // post which all pending group requests are also executed. 226 // post which all pending group requests are also executed.
227 + TestGroupKey key = new TestGroupKey("group1BeforeAudit");
196 Group createdGroup = groupService.getGroup(DID, key); 228 Group createdGroup = groupService.getGroup(DID, key);
197 int createdGroupId = createdGroup.id().id(); 229 int createdGroupId = createdGroup.id().id();
198 assertNotEquals(gId1.id(), createdGroupId); 230 assertNotEquals(gId1.id(), createdGroupId);
199 assertNotEquals(gId2.id(), createdGroupId); 231 assertNotEquals(gId2.id(), createdGroupId);
232 +
200 List<GroupOperation> expectedGroupOps = Arrays.asList( 233 List<GroupOperation> expectedGroupOps = Arrays.asList(
201 GroupOperation.createDeleteGroupOperation(gId1, 234 GroupOperation.createDeleteGroupOperation(gId1,
202 Group.Type.SELECT), 235 Group.Type.SELECT),
203 GroupOperation.createAddGroupOperation( 236 GroupOperation.createAddGroupOperation(
204 createdGroup.id(), 237 createdGroup.id(),
205 Group.Type.SELECT, 238 Group.Type.SELECT,
206 - groupBuckets)); 239 + createdGroup.buckets()));
207 internalProvider.validate(DID, expectedGroupOps); 240 internalProvider.validate(DID, expectedGroupOps);
241 + }
208 242
209 - group1 = createSouthboundGroupEntry(gId1, 243 + // Test AUDIT process with extraneous groups and missing groups
244 + private void testAuditWithExtraneousMissingGroups() {
245 + PortNumber[] ports1 = {PortNumber.portNumber(31),
246 + PortNumber.portNumber(32)};
247 + PortNumber[] ports2 = {PortNumber.portNumber(41),
248 + PortNumber.portNumber(42)};
249 + GroupId gId1 = new DefaultGroupId(1);
250 + Group group1 = createSouthboundGroupEntry(gId1,
210 Arrays.asList(ports1), 251 Arrays.asList(ports1),
211 0); 252 0);
212 - group2 = createSouthboundGroupEntry(gId2, 253 + GroupId gId2 = new DefaultGroupId(2);
254 + Group group2 = createSouthboundGroupEntry(gId2,
213 Arrays.asList(ports2), 255 Arrays.asList(ports2),
214 0); 256 0);
215 - groupEntries = Arrays.asList(group1, group2); 257 + List<Group> groupEntries = Arrays.asList(group1, group2);
216 providerService.pushGroupMetrics(DID, groupEntries); 258 providerService.pushGroupMetrics(DID, groupEntries);
217 - expectedGroupOps = Arrays.asList( 259 + TestGroupKey key = new TestGroupKey("group1BeforeAudit");
260 + Group createdGroup = groupService.getGroup(DID, key);
261 + List<GroupOperation> expectedGroupOps = Arrays.asList(
218 GroupOperation.createDeleteGroupOperation(gId1, 262 GroupOperation.createDeleteGroupOperation(gId1,
219 Group.Type.SELECT), 263 Group.Type.SELECT),
220 GroupOperation.createDeleteGroupOperation(gId2, 264 GroupOperation.createDeleteGroupOperation(gId2,
221 Group.Type.SELECT), 265 Group.Type.SELECT),
222 GroupOperation.createAddGroupOperation(createdGroup.id(), 266 GroupOperation.createAddGroupOperation(createdGroup.id(),
223 Group.Type.SELECT, 267 Group.Type.SELECT,
224 - groupBuckets)); 268 + createdGroup.buckets()));
225 internalProvider.validate(DID, expectedGroupOps); 269 internalProvider.validate(DID, expectedGroupOps);
270 + }
226 271
272 + // Test AUDIT with confirmed groups
273 + private void testAuditWithConfirmedGroups() {
274 + TestGroupKey key = new TestGroupKey("group1BeforeAudit");
275 + Group createdGroup = groupService.getGroup(DID, key);
227 createdGroup = new DefaultGroup(createdGroup.id(), 276 createdGroup = new DefaultGroup(createdGroup.id(),
228 DID, 277 DID,
229 Group.Type.SELECT, 278 Group.Type.SELECT,
230 - groupBuckets); 279 + createdGroup.buckets());
231 - groupEntries = Arrays.asList(createdGroup); 280 + List<Group> groupEntries = Arrays.asList(createdGroup);
232 providerService.pushGroupMetrics(DID, groupEntries); 281 providerService.pushGroupMetrics(DID, groupEntries);
233 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED)); 282 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
283 + }
234 284
235 // Test group add bucket operations 285 // Test group add bucket operations
286 + private void testAddBuckets() {
236 TestGroupKey addKey = new TestGroupKey("group1AddBuckets"); 287 TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
288 +
289 + TestGroupKey prevKey = new TestGroupKey("group1BeforeAudit");
290 + Group createdGroup = groupService.getGroup(DID, prevKey);
291 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
292 + buckets.addAll(createdGroup.buckets().buckets());
293 +
237 PortNumber[] addPorts = {PortNumber.portNumber(51), 294 PortNumber[] addPorts = {PortNumber.portNumber(51),
238 PortNumber.portNumber(52)}; 295 PortNumber.portNumber(52)};
239 - outPorts.clear(); 296 + List<PortNumber> outPorts = new ArrayList<PortNumber>();
240 outPorts.addAll(Arrays.asList(addPorts)); 297 outPorts.addAll(Arrays.asList(addPorts));
241 List<GroupBucket> addBuckets = new ArrayList<GroupBucket>(); 298 List<GroupBucket> addBuckets = new ArrayList<GroupBucket>();
242 for (PortNumber portNumber: outPorts) { 299 for (PortNumber portNumber: outPorts) {
...@@ -253,26 +310,34 @@ public class GroupManagerTest { ...@@ -253,26 +310,34 @@ public class GroupManagerTest {
253 } 310 }
254 GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets); 311 GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
255 groupService.addBucketsToGroup(DID, 312 groupService.addBucketsToGroup(DID,
256 - key, 313 + prevKey,
257 groupAddBuckets, 314 groupAddBuckets,
258 addKey, 315 addKey,
259 appId); 316 appId);
260 GroupBuckets updatedBuckets = new GroupBuckets(buckets); 317 GroupBuckets updatedBuckets = new GroupBuckets(buckets);
261 - expectedGroupOps = Arrays.asList( 318 + List<GroupOperation> expectedGroupOps = Arrays.asList(
262 GroupOperation.createModifyGroupOperation(createdGroup.id(), 319 GroupOperation.createModifyGroupOperation(createdGroup.id(),
263 Group.Type.SELECT, 320 Group.Type.SELECT,
264 updatedBuckets)); 321 updatedBuckets));
265 internalProvider.validate(DID, expectedGroupOps); 322 internalProvider.validate(DID, expectedGroupOps);
266 Group existingGroup = groupService.getGroup(DID, addKey); 323 Group existingGroup = groupService.getGroup(DID, addKey);
267 - groupEntries = Arrays.asList(existingGroup); 324 + List<Group> groupEntries = Arrays.asList(existingGroup);
268 providerService.pushGroupMetrics(DID, groupEntries); 325 providerService.pushGroupMetrics(DID, groupEntries);
269 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED)); 326 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
327 + }
270 328
271 // Test group remove bucket operations 329 // Test group remove bucket operations
330 + private void testRemoveBuckets() {
272 TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets"); 331 TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
332 +
333 + TestGroupKey prevKey = new TestGroupKey("group1AddBuckets");
334 + Group createdGroup = groupService.getGroup(DID, prevKey);
335 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
336 + buckets.addAll(createdGroup.buckets().buckets());
337 +
273 PortNumber[] removePorts = {PortNumber.portNumber(31), 338 PortNumber[] removePorts = {PortNumber.portNumber(31),
274 PortNumber.portNumber(32)}; 339 PortNumber.portNumber(32)};
275 - outPorts.clear(); 340 + List<PortNumber> outPorts = new ArrayList<PortNumber>();
276 outPorts.addAll(Arrays.asList(removePorts)); 341 outPorts.addAll(Arrays.asList(removePorts));
277 List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>(); 342 List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>();
278 for (PortNumber portNumber: outPorts) { 343 for (PortNumber portNumber: outPorts) {
...@@ -289,28 +354,32 @@ public class GroupManagerTest { ...@@ -289,28 +354,32 @@ public class GroupManagerTest {
289 } 354 }
290 GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets); 355 GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
291 groupService.removeBucketsFromGroup(DID, 356 groupService.removeBucketsFromGroup(DID,
292 - addKey, 357 + prevKey,
293 groupRemoveBuckets, 358 groupRemoveBuckets,
294 removeKey, 359 removeKey,
295 appId); 360 appId);
296 - updatedBuckets = new GroupBuckets(buckets); 361 + GroupBuckets updatedBuckets = new GroupBuckets(buckets);
297 - expectedGroupOps = Arrays.asList( 362 + List<GroupOperation> expectedGroupOps = Arrays.asList(
298 GroupOperation.createModifyGroupOperation(createdGroup.id(), 363 GroupOperation.createModifyGroupOperation(createdGroup.id(),
299 Group.Type.SELECT, 364 Group.Type.SELECT,
300 updatedBuckets)); 365 updatedBuckets));
301 internalProvider.validate(DID, expectedGroupOps); 366 internalProvider.validate(DID, expectedGroupOps);
302 - existingGroup = groupService.getGroup(DID, removeKey); 367 + Group existingGroup = groupService.getGroup(DID, removeKey);
303 - groupEntries = Arrays.asList(existingGroup); 368 + List<Group> groupEntries = Arrays.asList(existingGroup);
304 providerService.pushGroupMetrics(DID, groupEntries); 369 providerService.pushGroupMetrics(DID, groupEntries);
305 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED)); 370 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
371 + }
306 372
307 // Test group remove operations 373 // Test group remove operations
308 - groupService.removeGroup(DID, removeKey, appId); 374 + private void testRemoveGroup() {
309 - expectedGroupOps = Arrays.asList( 375 + TestGroupKey currKey = new TestGroupKey("group1RemoveBuckets");
310 - GroupOperation.createDeleteGroupOperation(createdGroup.id(), 376 + Group existingGroup = groupService.getGroup(DID, currKey);
377 + groupService.removeGroup(DID, currKey, appId);
378 + List<GroupOperation> expectedGroupOps = Arrays.asList(
379 + GroupOperation.createDeleteGroupOperation(existingGroup.id(),
311 Group.Type.SELECT)); 380 Group.Type.SELECT));
312 internalProvider.validate(DID, expectedGroupOps); 381 internalProvider.validate(DID, expectedGroupOps);
313 - groupEntries = Collections.emptyList(); 382 + List<Group> groupEntries = Collections.emptyList();
314 providerService.pushGroupMetrics(DID, groupEntries); 383 providerService.pushGroupMetrics(DID, groupEntries);
315 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED)); 384 internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
316 } 385 }
......
...@@ -261,6 +261,11 @@ public class SimpleGroupStore ...@@ -261,6 +261,11 @@ public class SimpleGroupStore
261 } 261 }
262 262
263 private void storeGroupDescriptionInternal(GroupDescription groupDesc) { 263 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
264 + // Check if a group is existing with the same key
265 + if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
266 + return;
267 + }
268 +
264 // Get a new group identifier 269 // Get a new group identifier
265 GroupId id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId())); 270 GroupId id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
266 // Create a group entry object 271 // Create a group entry object
...@@ -448,9 +453,13 @@ public class SimpleGroupStore ...@@ -448,9 +453,13 @@ public class SimpleGroupStore
448 } 453 }
449 454
450 @Override 455 @Override
451 - public void deviceInitialAuditCompleted(DeviceId deviceId) { 456 + public void deviceInitialAuditCompleted(DeviceId deviceId,
457 + boolean completed) {
452 synchronized (deviceAuditStatus) { 458 synchronized (deviceAuditStatus) {
453 - deviceAuditStatus.putIfAbsent(deviceId, true); 459 + if (completed) {
460 + log.debug("deviceInitialAuditCompleted: AUDIT "
461 + + "completed for device {}", deviceId);
462 + deviceAuditStatus.put(deviceId, true);
454 // Execute all pending group requests 463 // Execute all pending group requests
455 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests = 464 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
456 getPendingGroupKeyTable(deviceId); 465 getPendingGroupKeyTable(deviceId);
...@@ -464,13 +473,21 @@ public class SimpleGroupStore ...@@ -464,13 +473,21 @@ public class SimpleGroupStore
464 storeGroupDescriptionInternal(tmp); 473 storeGroupDescriptionInternal(tmp);
465 } 474 }
466 getPendingGroupKeyTable(deviceId).clear(); 475 getPendingGroupKeyTable(deviceId).clear();
476 + } else {
477 + if (deviceAuditStatus.get(deviceId)) {
478 + log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
479 + + "status for device {}", deviceId);
480 + deviceAuditStatus.put(deviceId, false);
481 + }
482 + }
467 } 483 }
468 } 484 }
469 485
470 @Override 486 @Override
471 public boolean deviceInitialAuditStatus(DeviceId deviceId) { 487 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
472 synchronized (deviceAuditStatus) { 488 synchronized (deviceAuditStatus) {
473 - return (deviceAuditStatus.get(deviceId) != null) ? true : false; 489 + return (deviceAuditStatus.get(deviceId) != null)
490 + ? deviceAuditStatus.get(deviceId) : false;
474 } 491 }
475 } 492 }
476 493
......
...@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; ...@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
19 import static org.onosproject.net.DeviceId.deviceId; 19 import static org.onosproject.net.DeviceId.deviceId;
20 20
21 import java.util.ArrayList; 21 import java.util.ArrayList;
22 +import java.util.Arrays;
22 import java.util.List; 23 import java.util.List;
23 24
24 import org.junit.After; 25 import org.junit.After;
...@@ -53,6 +54,8 @@ import com.google.common.collect.Iterables; ...@@ -53,6 +54,8 @@ import com.google.common.collect.Iterables;
53 public class SimpleGroupStoreTest { 54 public class SimpleGroupStoreTest {
54 55
55 private SimpleGroupStore simpleGroupStore; 56 private SimpleGroupStore simpleGroupStore;
57 + private final ApplicationId appId =
58 + new DefaultApplicationId(2, "org.groupstore.test");
56 59
57 public static final DeviceId D1 = deviceId("of:1"); 60 public static final DeviceId D1 = deviceId("of:1");
58 61
...@@ -167,16 +170,42 @@ public class SimpleGroupStoreTest { ...@@ -167,16 +170,42 @@ public class SimpleGroupStoreTest {
167 @Test 170 @Test
168 public void testGroupStoreOperations() { 171 public void testGroupStoreOperations() {
169 // Set the Device AUDIT completed in the store 172 // Set the Device AUDIT completed in the store
170 - simpleGroupStore.deviceInitialAuditCompleted(D1); 173 + simpleGroupStore.deviceInitialAuditCompleted(D1, true);
171 174
172 - ApplicationId appId = 175 + // Testing storeGroup operation
173 - new DefaultApplicationId(2, "org.groupstore.test"); 176 + TestGroupKey newKey = new TestGroupKey("group1");
174 - TestGroupKey key = new TestGroupKey("group1"); 177 + testStoreAndGetGroup(newKey);
178 +
179 + // Testing addOrUpdateGroupEntry operation from southbound
180 + TestGroupKey currKey = newKey;
181 + testAddGroupEntryFromSB(currKey);
182 +
183 + // Testing updateGroupDescription for ADD operation from northbound
184 + newKey = new TestGroupKey("group1AddBuckets");
185 + testAddBuckets(currKey, newKey);
186 +
187 + // Testing updateGroupDescription for REMOVE operation from northbound
188 + currKey = newKey;
189 + newKey = new TestGroupKey("group1RemoveBuckets");
190 + testRemoveBuckets(currKey, newKey);
191 +
192 + // Testing addOrUpdateGroupEntry operation from southbound
193 + currKey = newKey;
194 + testUpdateGroupEntryFromSB(currKey);
195 +
196 + // Testing deleteGroupDescription operation from northbound
197 + testDeleteGroup(currKey);
198 +
199 + // Testing removeGroupEntry operation from southbound
200 + testRemoveGroupFromSB(currKey);
201 + }
202 +
203 + // Testing storeGroup operation
204 + private void testStoreAndGetGroup(TestGroupKey key) {
175 PortNumber[] ports = {PortNumber.portNumber(31), 205 PortNumber[] ports = {PortNumber.portNumber(31),
176 PortNumber.portNumber(32)}; 206 PortNumber.portNumber(32)};
177 List<PortNumber> outPorts = new ArrayList<PortNumber>(); 207 List<PortNumber> outPorts = new ArrayList<PortNumber>();
178 - outPorts.add(ports[0]); 208 + outPorts.addAll(Arrays.asList(ports));
179 - outPorts.add(ports[1]);
180 209
181 List<GroupBucket> buckets = new ArrayList<GroupBucket>(); 210 List<GroupBucket> buckets = new ArrayList<GroupBucket>();
182 for (PortNumber portNumber: outPorts) { 211 for (PortNumber portNumber: outPorts) {
...@@ -220,23 +249,44 @@ public class SimpleGroupStoreTest { ...@@ -220,23 +249,44 @@ public class SimpleGroupStoreTest {
220 } 249 }
221 assertEquals(1, groupCount); 250 assertEquals(1, groupCount);
222 simpleGroupStore.unsetDelegate(checkStoreGroupDelegate); 251 simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
252 + }
223 253
224 // Testing addOrUpdateGroupEntry operation from southbound 254 // Testing addOrUpdateGroupEntry operation from southbound
255 + private void testAddGroupEntryFromSB(TestGroupKey currKey) {
256 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
257 +
225 InternalGroupStoreDelegate addGroupEntryDelegate = 258 InternalGroupStoreDelegate addGroupEntryDelegate =
226 - new InternalGroupStoreDelegate(key, 259 + new InternalGroupStoreDelegate(currKey,
227 - groupBuckets, 260 + existingGroup.buckets(),
228 GroupEvent.Type.GROUP_ADDED); 261 GroupEvent.Type.GROUP_ADDED);
229 simpleGroupStore.setDelegate(addGroupEntryDelegate); 262 simpleGroupStore.setDelegate(addGroupEntryDelegate);
230 - simpleGroupStore.addOrUpdateGroupEntry(createdGroup); 263 + simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
231 simpleGroupStore.unsetDelegate(addGroupEntryDelegate); 264 simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
265 + }
266 +
267 + // Testing addOrUpdateGroupEntry operation from southbound
268 + private void testUpdateGroupEntryFromSB(TestGroupKey currKey) {
269 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
270 +
271 + InternalGroupStoreDelegate updateGroupEntryDelegate =
272 + new InternalGroupStoreDelegate(currKey,
273 + existingGroup.buckets(),
274 + GroupEvent.Type.GROUP_UPDATED);
275 + simpleGroupStore.setDelegate(updateGroupEntryDelegate);
276 + simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
277 + simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
278 + }
232 279
233 // Testing updateGroupDescription for ADD operation from northbound 280 // Testing updateGroupDescription for ADD operation from northbound
234 - TestGroupKey addKey = new TestGroupKey("group1AddBuckets"); 281 + private void testAddBuckets(TestGroupKey currKey, TestGroupKey addKey) {
282 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
283 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
284 + buckets.addAll(existingGroup.buckets().buckets());
285 +
235 PortNumber[] newNeighborPorts = {PortNumber.portNumber(41), 286 PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
236 PortNumber.portNumber(42)}; 287 PortNumber.portNumber(42)};
237 List<PortNumber> newOutPorts = new ArrayList<PortNumber>(); 288 List<PortNumber> newOutPorts = new ArrayList<PortNumber>();
238 - newOutPorts.add(newNeighborPorts[0]); 289 + newOutPorts.addAll(Arrays.asList(newNeighborPorts[0]));
239 - newOutPorts.add(newNeighborPorts[1]);
240 290
241 List<GroupBucket> toAddBuckets = new ArrayList<GroupBucket>(); 291 List<GroupBucket> toAddBuckets = new ArrayList<GroupBucket>();
242 for (PortNumber portNumber: newOutPorts) { 292 for (PortNumber portNumber: newOutPorts) {
...@@ -258,79 +308,75 @@ public class SimpleGroupStoreTest { ...@@ -258,79 +308,75 @@ public class SimpleGroupStoreTest {
258 GroupEvent.Type.GROUP_UPDATE_REQUESTED); 308 GroupEvent.Type.GROUP_UPDATE_REQUESTED);
259 simpleGroupStore.setDelegate(updateGroupDescDelegate); 309 simpleGroupStore.setDelegate(updateGroupDescDelegate);
260 simpleGroupStore.updateGroupDescription(D1, 310 simpleGroupStore.updateGroupDescription(D1,
261 - key, 311 + currKey,
262 UpdateType.ADD, 312 UpdateType.ADD,
263 toAddGroupBuckets, 313 toAddGroupBuckets,
264 addKey); 314 addKey);
265 simpleGroupStore.unsetDelegate(updateGroupDescDelegate); 315 simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
316 + }
266 317
267 // Testing updateGroupDescription for REMOVE operation from northbound 318 // Testing updateGroupDescription for REMOVE operation from northbound
268 - TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets"); 319 + private void testRemoveBuckets(TestGroupKey currKey, TestGroupKey removeKey) {
320 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
321 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
322 + buckets.addAll(existingGroup.buckets().buckets());
323 +
269 List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>(); 324 List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
270 - toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0)); 325 +
271 - toRemoveBuckets.add(updatedGroupBuckets.buckets().get(1)); 326 + // There should be 4 buckets in the current group
327 + toRemoveBuckets.add(buckets.remove(0));
328 + toRemoveBuckets.add(buckets.remove(1));
272 GroupBuckets toRemoveGroupBuckets = new GroupBuckets(toRemoveBuckets); 329 GroupBuckets toRemoveGroupBuckets = new GroupBuckets(toRemoveBuckets);
273 - List<GroupBucket> remainingBuckets = new ArrayList<GroupBucket>(); 330 +
274 - remainingBuckets.add(updatedGroupBuckets.buckets().get(2)); 331 + GroupBuckets remainingGroupBuckets = new GroupBuckets(buckets);
275 - remainingBuckets.add(updatedGroupBuckets.buckets().get(3));
276 - GroupBuckets remainingGroupBuckets = new GroupBuckets(remainingBuckets);
277 InternalGroupStoreDelegate removeGroupDescDelegate = 332 InternalGroupStoreDelegate removeGroupDescDelegate =
278 new InternalGroupStoreDelegate(removeKey, 333 new InternalGroupStoreDelegate(removeKey,
279 remainingGroupBuckets, 334 remainingGroupBuckets,
280 GroupEvent.Type.GROUP_UPDATE_REQUESTED); 335 GroupEvent.Type.GROUP_UPDATE_REQUESTED);
281 simpleGroupStore.setDelegate(removeGroupDescDelegate); 336 simpleGroupStore.setDelegate(removeGroupDescDelegate);
282 simpleGroupStore.updateGroupDescription(D1, 337 simpleGroupStore.updateGroupDescription(D1,
283 - addKey, 338 + currKey,
284 UpdateType.REMOVE, 339 UpdateType.REMOVE,
285 toRemoveGroupBuckets, 340 toRemoveGroupBuckets,
286 removeKey); 341 removeKey);
287 simpleGroupStore.unsetDelegate(removeGroupDescDelegate); 342 simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
288 - 343 + }
289 - // Testing getGroup operation
290 - Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
291 - checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
292 -
293 - // Testing addOrUpdateGroupEntry operation from southbound
294 - InternalGroupStoreDelegate updateGroupEntryDelegate =
295 - new InternalGroupStoreDelegate(removeKey,
296 - remainingGroupBuckets,
297 - GroupEvent.Type.GROUP_UPDATED);
298 - simpleGroupStore.setDelegate(updateGroupEntryDelegate);
299 - simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
300 - simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
301 344
302 // Testing deleteGroupDescription operation from northbound 345 // Testing deleteGroupDescription operation from northbound
346 + private void testDeleteGroup(TestGroupKey currKey) {
347 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
303 InternalGroupStoreDelegate deleteGroupDescDelegate = 348 InternalGroupStoreDelegate deleteGroupDescDelegate =
304 - new InternalGroupStoreDelegate(removeKey, 349 + new InternalGroupStoreDelegate(currKey,
305 - remainingGroupBuckets, 350 + existingGroup.buckets(),
306 GroupEvent.Type.GROUP_REMOVE_REQUESTED); 351 GroupEvent.Type.GROUP_REMOVE_REQUESTED);
307 simpleGroupStore.setDelegate(deleteGroupDescDelegate); 352 simpleGroupStore.setDelegate(deleteGroupDescDelegate);
308 - simpleGroupStore.deleteGroupDescription(D1, removeKey); 353 + simpleGroupStore.deleteGroupDescription(D1, currKey);
309 simpleGroupStore.unsetDelegate(deleteGroupDescDelegate); 354 simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
355 + }
310 356
311 // Testing removeGroupEntry operation from southbound 357 // Testing removeGroupEntry operation from southbound
358 + private void testRemoveGroupFromSB(TestGroupKey currKey) {
359 + Group existingGroup = simpleGroupStore.getGroup(D1, currKey);
312 InternalGroupStoreDelegate removeGroupEntryDelegate = 360 InternalGroupStoreDelegate removeGroupEntryDelegate =
313 - new InternalGroupStoreDelegate(removeKey, 361 + new InternalGroupStoreDelegate(currKey,
314 - remainingGroupBuckets, 362 + existingGroup.buckets(),
315 GroupEvent.Type.GROUP_REMOVED); 363 GroupEvent.Type.GROUP_REMOVED);
316 simpleGroupStore.setDelegate(removeGroupEntryDelegate); 364 simpleGroupStore.setDelegate(removeGroupEntryDelegate);
317 simpleGroupStore.removeGroupEntry(existingGroup); 365 simpleGroupStore.removeGroupEntry(existingGroup);
318 366
319 // Testing getGroup operation 367 // Testing getGroup operation
320 - existingGroup = simpleGroupStore.getGroup(D1, removeKey); 368 + existingGroup = simpleGroupStore.getGroup(D1, currKey);
321 assertEquals(null, existingGroup); 369 assertEquals(null, existingGroup);
322 assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1))); 370 assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1)));
323 assertEquals(0, simpleGroupStore.getGroupCount(D1)); 371 assertEquals(0, simpleGroupStore.getGroupCount(D1));
324 372
325 simpleGroupStore.unsetDelegate(removeGroupEntryDelegate); 373 simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
326 -
327 -
328 } 374 }
329 375
330 @Test 376 @Test
331 public void testGroupOperationFailure() { 377 public void testGroupOperationFailure() {
332 378
333 - simpleGroupStore.deviceInitialAuditCompleted(D1); 379 + simpleGroupStore.deviceInitialAuditCompleted(D1, true);
334 380
335 ApplicationId appId = 381 ApplicationId appId =
336 new DefaultApplicationId(2, "org.groupstore.test"); 382 new DefaultApplicationId(2, "org.groupstore.test");
...@@ -408,8 +454,6 @@ public class SimpleGroupStoreTest { ...@@ -408,8 +454,6 @@ public class SimpleGroupStoreTest {
408 GroupEvent.Type.GROUP_REMOVE_FAILED); 454 GroupEvent.Type.GROUP_REMOVE_FAILED);
409 simpleGroupStore.setDelegate(checkGroupDelFailureDelegate); 455 simpleGroupStore.setDelegate(checkGroupDelFailureDelegate);
410 simpleGroupStore.groupOperationFailed(D1, groupDelOp); 456 simpleGroupStore.groupOperationFailed(D1, groupDelOp);
411 -
412 -
413 } 457 }
414 } 458 }
415 459
......