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
apps/grouphandler/pom.xml
0 → 100644
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> |
apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultEdgeGroupHandler.java
0 → 100644
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 |
apps/grouphandler/src/main/java/org/onosproject/grouphandler/DefaultTransitGroupHandler.java
0 → 100644
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 | ... | ... |
-
Please register or login to post a comment