Srikanth Vavilapalli
Committed by Ali "The Bomb" Al-Shabibi

ONOS-895: Group manager implementation

Change-Id: Ie183f722fa39012f8de056961715c325e2388e63
...@@ -37,7 +37,7 @@ public class DefaultGroupId implements GroupId { ...@@ -37,7 +37,7 @@ public class DefaultGroupId implements GroupId {
37 37
38 @Override 38 @Override
39 public int id() { 39 public int id() {
40 - return 0; 40 + return this.id;
41 } 41 }
42 42
43 @Override 43 @Override
......
...@@ -17,7 +17,10 @@ package org.onosproject.net.group; ...@@ -17,7 +17,10 @@ package org.onosproject.net.group;
17 17
18 import static org.slf4j.LoggerFactory.getLogger; 18 import static org.slf4j.LoggerFactory.getLogger;
19 19
20 +import java.util.Objects;
21 +
20 import org.onosproject.core.GroupId; 22 import org.onosproject.core.GroupId;
23 +import org.onosproject.net.DeviceId;
21 import org.slf4j.Logger; 24 import org.slf4j.Logger;
22 25
23 /** 26 /**
...@@ -32,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription ...@@ -32,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription
32 private long life; 35 private long life;
33 private long packets; 36 private long packets;
34 private long bytes; 37 private long bytes;
38 + private long referenceCount;
35 private GroupId id; 39 private GroupId id;
36 40
37 /** 41 /**
...@@ -47,6 +51,29 @@ public class DefaultGroup extends DefaultGroupDescription ...@@ -47,6 +51,29 @@ public class DefaultGroup extends DefaultGroupDescription
47 this.life = 0; 51 this.life = 0;
48 this.packets = 0; 52 this.packets = 0;
49 this.bytes = 0; 53 this.bytes = 0;
54 + this.referenceCount = 0;
55 + }
56 +
57 + /**
58 + * Default group object constructor with the available information
59 + * from data plane.
60 + *
61 + * @param id group identifier
62 + * @param deviceId device identifier
63 + * @param type type of the group
64 + * @param buckets immutable list of group bucket
65 + */
66 + public DefaultGroup(GroupId id,
67 + DeviceId deviceId,
68 + GroupDescription.Type type,
69 + GroupBuckets buckets) {
70 + super(deviceId, type, buckets);
71 + this.id = id;
72 + this.state = GroupState.PENDING_ADD;
73 + this.life = 0;
74 + this.packets = 0;
75 + this.bytes = 0;
76 + this.referenceCount = 0;
50 } 77 }
51 78
52 /** 79 /**
...@@ -139,4 +166,43 @@ public class DefaultGroup extends DefaultGroupDescription ...@@ -139,4 +166,43 @@ public class DefaultGroup extends DefaultGroupDescription
139 this.bytes = bytes; 166 this.bytes = bytes;
140 } 167 }
141 168
169 + @Override
170 + public void setReferenceCount(long referenceCount) {
171 + this.referenceCount = referenceCount;
172 + }
173 +
174 + @Override
175 + public long referenceCount() {
176 + return referenceCount;
177 + }
178 +
179 + /*
180 + * The deviceId, type and buckets are used for hash.
181 + *
182 + * (non-Javadoc)
183 + * @see java.lang.Object#equals(java.lang.Object)
184 + */
185 + @Override
186 + public int hashCode() {
187 + return super.hashCode() + Objects.hash(id);
188 + }
189 +
190 + /*
191 + * The deviceId, groupId, type and buckets should be same.
192 + *
193 + * (non-Javadoc)
194 + * @see java.lang.Object#equals(java.lang.Object)
195 + */
196 + @Override
197 + public boolean equals(Object obj) {
198 + if (this == obj) {
199 + return true;
200 + }
201 + if (obj instanceof DefaultGroup) {
202 + DefaultGroup that = (DefaultGroup) obj;
203 + return super.equals(obj) &&
204 + Objects.equals(id, that.id);
205 + }
206 + return false;
207 + }
142 } 208 }
......
...@@ -18,6 +18,8 @@ package org.onosproject.net.group; ...@@ -18,6 +18,8 @@ package org.onosproject.net.group;
18 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkArgument;
19 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.base.Preconditions.checkNotNull;
20 20
21 +import java.util.Objects;
22 +
21 import org.onosproject.core.GroupId; 23 import org.onosproject.core.GroupId;
22 import org.onosproject.net.PortNumber; 24 import org.onosproject.net.PortNumber;
23 import org.onosproject.net.flow.TrafficTreatment; 25 import org.onosproject.net.flow.TrafficTreatment;
...@@ -178,4 +180,36 @@ public final class DefaultGroupBucket implements GroupBucket { ...@@ -178,4 +180,36 @@ public final class DefaultGroupBucket implements GroupBucket {
178 public GroupId watchGroup() { 180 public GroupId watchGroup() {
179 return watchGroup; 181 return watchGroup;
180 } 182 }
183 +
184 + /*
185 + * The type and treatment can change on a given bucket
186 + *
187 + * (non-Javadoc)
188 + * @see java.lang.Object#equals(java.lang.Object)
189 + */
190 + @Override
191 + public int hashCode() {
192 + return Objects.hash(type, treatment);
193 + }
194 +
195 + /*
196 + * The priority and statistics can change on a given treatment and selector
197 + *
198 + * (non-Javadoc)
199 + * @see java.lang.Object#equals(java.lang.Object)
200 + */
201 + @Override
202 + public boolean equals(Object obj) {
203 + if (this == obj) {
204 + return true;
205 + }
206 + if (obj instanceof DefaultGroupBucket) {
207 + DefaultGroupBucket that = (DefaultGroupBucket) obj;
208 + return Objects.equals(type, that.type) &&
209 + this.treatment.instructions().containsAll(that.treatment.instructions()) &&
210 + that.treatment.instructions().containsAll(this.treatment.instructions());
211 + }
212 + return false;
213 + }
214 +
181 } 215 }
......
...@@ -17,6 +17,8 @@ package org.onosproject.net.group; ...@@ -17,6 +17,8 @@ package org.onosproject.net.group;
17 17
18 import static com.google.common.base.Preconditions.checkNotNull; 18 import static com.google.common.base.Preconditions.checkNotNull;
19 19
20 +import java.util.Objects;
21 +
20 import org.onosproject.core.ApplicationId; 22 import org.onosproject.core.ApplicationId;
21 import org.onosproject.net.DeviceId; 23 import org.onosproject.net.DeviceId;
22 24
...@@ -49,8 +51,8 @@ public class DefaultGroupDescription implements GroupDescription { ...@@ -49,8 +51,8 @@ public class DefaultGroupDescription implements GroupDescription {
49 this.type = checkNotNull(type); 51 this.type = checkNotNull(type);
50 this.deviceId = checkNotNull(deviceId); 52 this.deviceId = checkNotNull(deviceId);
51 this.buckets = checkNotNull(buckets); 53 this.buckets = checkNotNull(buckets);
52 - this.appCookie = checkNotNull(appCookie); 54 + this.appCookie = appCookie;
53 - this.appId = checkNotNull(appId); 55 + this.appId = appId;
54 } 56 }
55 57
56 /** 58 /**
...@@ -61,11 +63,27 @@ public class DefaultGroupDescription implements GroupDescription { ...@@ -61,11 +63,27 @@ public class DefaultGroupDescription implements GroupDescription {
61 * 63 *
62 */ 64 */
63 public DefaultGroupDescription(GroupDescription groupDesc) { 65 public DefaultGroupDescription(GroupDescription groupDesc) {
64 - this.type = checkNotNull(groupDesc.type()); 66 + this.type = groupDesc.type();
65 - this.deviceId = checkNotNull(groupDesc.deviceId()); 67 + this.deviceId = groupDesc.deviceId();
66 - this.buckets = checkNotNull(groupDesc.buckets()); 68 + this.buckets = groupDesc.buckets();
67 - this.appCookie = checkNotNull(groupDesc.appCookie()); 69 + this.appCookie = groupDesc.appCookie();
68 - this.appId = checkNotNull(groupDesc.appId()); 70 + this.appId = groupDesc.appId();
71 + }
72 +
73 + /**
74 + * Constructor to be used by group subsystem internal components.
75 + * Creates group description object from the information retrieved
76 + * from data plane.
77 + *
78 + * @param deviceId device identifier
79 + * @param type type of the group
80 + * @param buckets immutable list of group bucket
81 + *
82 + */
83 + public DefaultGroupDescription(DeviceId deviceId,
84 + GroupDescription.Type type,
85 + GroupBuckets buckets) {
86 + this(deviceId, type, buckets, null, null);
69 } 87 }
70 88
71 /** 89 /**
...@@ -118,4 +136,36 @@ public class DefaultGroupDescription implements GroupDescription { ...@@ -118,4 +136,36 @@ public class DefaultGroupDescription implements GroupDescription {
118 return this.buckets; 136 return this.buckets;
119 } 137 }
120 138
139 + @Override
140 + /*
141 + * The deviceId, type and buckets are used for hash.
142 + *
143 + * (non-Javadoc)
144 + * @see java.lang.Object#equals(java.lang.Object)
145 + */
146 + public int hashCode() {
147 + return Objects.hash(deviceId, type, buckets);
148 + }
149 +
150 + @Override
151 + /*
152 + * The deviceId, type and buckets should be same.
153 + *
154 + * (non-Javadoc)
155 + * @see java.lang.Object#equals(java.lang.Object)
156 + */
157 + public boolean equals(Object obj) {
158 + if (this == obj) {
159 + return true;
160 + }
161 + if (obj instanceof DefaultGroupDescription) {
162 + DefaultGroupDescription that = (DefaultGroupDescription) obj;
163 + return Objects.equals(deviceId, that.deviceId) &&
164 + Objects.equals(type, that.type) &&
165 + Objects.equals(buckets, that.buckets);
166 +
167 + }
168 + return false;
169 + }
170 +
121 } 171 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -26,6 +26,10 @@ public interface Group extends GroupDescription { ...@@ -26,6 +26,10 @@ public interface Group extends GroupDescription {
26 */ 26 */
27 public enum GroupState { 27 public enum GroupState {
28 /** 28 /**
29 + * Group create request is queued as group AUDIT is in progress.
30 + */
31 + WAITING_AUDIT_COMPLETE,
32 + /**
29 * Group create request is processed by ONOS and not yet 33 * Group create request is processed by ONOS and not yet
30 * received the confirmation from data plane. 34 * received the confirmation from data plane.
31 */ 35 */
...@@ -81,4 +85,11 @@ public interface Group extends GroupDescription { ...@@ -81,4 +85,11 @@ public interface Group extends GroupDescription {
81 * @return number of bytes 85 * @return number of bytes
82 */ 86 */
83 long bytes(); 87 long bytes();
88 +
89 + /**
90 + * Returns the number of flow rules or other groups reference this group.
91 + *
92 + * @return number of flow rules or other groups pointing to this group
93 + */
94 + long referenceCount();
84 } 95 }
......
...@@ -45,4 +45,25 @@ public final class GroupBuckets { ...@@ -45,4 +45,25 @@ public final class GroupBuckets {
45 return buckets; 45 return buckets;
46 } 46 }
47 47
48 + @Override
49 + public int hashCode() {
50 + int result = 17;
51 + int combinedHash = 0;
52 + for (GroupBucket bucket:buckets) {
53 + combinedHash = combinedHash + bucket.hashCode();
54 + }
55 + result = 31 * result + combinedHash;
56 +
57 + return result;
58 + }
59 +
60 + @Override
61 + public boolean equals(Object obj) {
62 + if (obj instanceof GroupBuckets) {
63 + return (this.buckets.containsAll(((GroupBuckets) obj).buckets) &&
64 + ((GroupBuckets) obj).buckets.containsAll(this.buckets));
65 + }
66 + return false;
67 + }
68 +
48 } 69 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -17,6 +17,8 @@ package org.onosproject.net.group; ...@@ -17,6 +17,8 @@ package org.onosproject.net.group;
17 17
18 import static com.google.common.base.Preconditions.checkNotNull; 18 import static com.google.common.base.Preconditions.checkNotNull;
19 19
20 +import java.util.Objects;
21 +
20 import org.onosproject.core.GroupId; 22 import org.onosproject.core.GroupId;
21 23
22 /** 24 /**
...@@ -142,4 +144,37 @@ public final class GroupOperation { ...@@ -142,4 +144,37 @@ public final class GroupOperation {
142 public GroupBuckets buckets() { 144 public GroupBuckets buckets() {
143 return this.buckets; 145 return this.buckets;
144 } 146 }
147 +
148 + @Override
149 + /*
150 + * The deviceId, type and buckets are used for hash.
151 + *
152 + * (non-Javadoc)
153 + * @see java.lang.Object#equals(java.lang.Object)
154 + */
155 + public int hashCode() {
156 + return (buckets != null) ? Objects.hash(groupId, opType, buckets) :
157 + Objects.hash(groupId, opType);
158 + }
159 +
160 + @Override
161 + /*
162 + * The deviceId, type and buckets should be same.
163 + *
164 + * (non-Javadoc)
165 + * @see java.lang.Object#equals(java.lang.Object)
166 + */
167 + public boolean equals(Object obj) {
168 + if (this == obj) {
169 + return true;
170 + }
171 + if (obj instanceof GroupOperation) {
172 + GroupOperation that = (GroupOperation) obj;
173 + return Objects.equals(groupId, that.groupId) &&
174 + Objects.equals(opType, that.opType) &&
175 + Objects.equals(buckets, that.buckets);
176 +
177 + }
178 + return false;
179 + }
145 } 180 }
...\ 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.net.group;
17 +
18 +import org.onosproject.net.provider.ProviderRegistry;
19 +
20 +/**
21 + * Abstraction for a group provider registry.
22 + */
23 +public interface GroupProviderRegistry
24 + extends ProviderRegistry<GroupProvider, GroupProviderService> {
25 +}
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
16 package org.onosproject.net.group; 16 package org.onosproject.net.group;
17 17
18 import org.onosproject.core.ApplicationId; 18 import org.onosproject.core.ApplicationId;
19 -import org.onosproject.net.Device;
20 import org.onosproject.net.DeviceId; 19 import org.onosproject.net.DeviceId;
21 20
22 /** 21 /**
...@@ -27,7 +26,7 @@ import org.onosproject.net.DeviceId; ...@@ -27,7 +26,7 @@ import org.onosproject.net.DeviceId;
27 * specified in a group. 26 * specified in a group.
28 * "group" can also be used for grouping common actions of different flows, 27 * "group" can also be used for grouping common actions of different flows,
29 * so that in some scenarios only one group entry required to be modified 28 * so that in some scenarios only one group entry required to be modified
30 - * for all the referencing flow entries instead of modifying all of them 29 + * for all the referencing flow entries instead of modifying all of them.
31 * 30 *
32 * This implements semantics of a distributed authoritative group store 31 * This implements semantics of a distributed authoritative group store
33 * where the master copy of the groups lies with the controller and 32 * where the master copy of the groups lies with the controller and
...@@ -60,7 +59,7 @@ public interface GroupService { ...@@ -60,7 +59,7 @@ public interface GroupService {
60 * NOTE1: The presence of group object in the system does not 59 * NOTE1: The presence of group object in the system does not
61 * guarantee that the "group" is actually created in device. 60 * guarantee that the "group" is actually created in device.
62 * GROUP_ADDED notification would confirm the creation of 61 * GROUP_ADDED notification would confirm the creation of
63 - * this group in data plane 62 + * this group in data plane.
64 * 63 *
65 * @param deviceId device identifier 64 * @param deviceId device identifier
66 * @param appCookie application cookie to be used for lookup 65 * @param appCookie application cookie to be used for lookup
...@@ -73,7 +72,7 @@ public interface GroupService { ...@@ -73,7 +72,7 @@ public interface GroupService {
73 * Appends buckets to existing group. The caller can optionally 72 * Appends buckets to existing group. The caller can optionally
74 * associate a new cookie during this updation. GROUP_UPDATED or 73 * associate a new cookie during this updation. GROUP_UPDATED or
75 * GROUP_UPDATE_FAILED notifications would be provided along with 74 * GROUP_UPDATE_FAILED notifications would be provided along with
76 - * cookie depending on the result of the operation on the device 75 + * cookie depending on the result of the operation on the device.
77 * 76 *
78 * @param deviceId device identifier 77 * @param deviceId device identifier
79 * @param oldCookie cookie to be used to retrieve the existing group 78 * @param oldCookie cookie to be used to retrieve the existing group
...@@ -91,7 +90,7 @@ public interface GroupService { ...@@ -91,7 +90,7 @@ public interface GroupService {
91 * Removes buckets from existing group. The caller can optionally 90 * Removes buckets from existing group. The caller can optionally
92 * associate a new cookie during this updation. GROUP_UPDATED or 91 * associate a new cookie during this updation. GROUP_UPDATED or
93 * GROUP_UPDATE_FAILED notifications would be provided along with 92 * GROUP_UPDATE_FAILED notifications would be provided along with
94 - * cookie depending on the result of the operation on the device 93 + * cookie depending on the result of the operation on the device.
95 * 94 *
96 * @param deviceId device identifier 95 * @param deviceId device identifier
97 * @param oldCookie cookie to be used to retrieve the existing group 96 * @param oldCookie cookie to be used to retrieve the existing group
...@@ -99,7 +98,7 @@ public interface GroupService { ...@@ -99,7 +98,7 @@ public interface GroupService {
99 * @param newCookie immutable cookie to be used post update operation 98 * @param newCookie immutable cookie to be used post update operation
100 * @param appId Application Id 99 * @param appId Application Id
101 */ 100 */
102 - void removeBucketsFromGroup(Device deviceId, 101 + void removeBucketsFromGroup(DeviceId deviceId,
103 GroupKey oldCookie, 102 GroupKey oldCookie,
104 GroupBuckets buckets, 103 GroupBuckets buckets,
105 GroupKey newCookie, 104 GroupKey newCookie,
...@@ -109,13 +108,13 @@ public interface GroupService { ...@@ -109,13 +108,13 @@ public interface GroupService {
109 * Deletes a group associated to an application cookie. 108 * Deletes a group associated to an application cookie.
110 * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be 109 * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
111 * provided along with cookie depending on the result of the 110 * provided along with cookie depending on the result of the
112 - * operation on the device 111 + * operation on the device.
113 * 112 *
114 * @param deviceId device identifier 113 * @param deviceId device identifier
115 * @param appCookie application cookie to be used for lookup 114 * @param appCookie application cookie to be used for lookup
116 * @param appId Application Id 115 * @param appId Application Id
117 */ 116 */
118 - void removeGroup(Device deviceId, GroupKey appCookie, ApplicationId appId); 117 + void removeGroup(DeviceId deviceId, GroupKey appCookie, ApplicationId appId);
119 118
120 /** 119 /**
121 * Retrieves all groups created by an application in the specified device 120 * Retrieves all groups created by an application in the specified device
...@@ -125,7 +124,7 @@ public interface GroupService { ...@@ -125,7 +124,7 @@ public interface GroupService {
125 * @param appId application id 124 * @param appId application id
126 * @return collection of immutable group objects created by the application 125 * @return collection of immutable group objects created by the application
127 */ 126 */
128 - Iterable<Group> getGroups(Device deviceId, ApplicationId appId); 127 + Iterable<Group> getGroups(DeviceId deviceId, ApplicationId appId);
129 128
130 /** 129 /**
131 * Adds the specified group listener. 130 * Adds the specified group listener.
......
...@@ -73,12 +73,14 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> { ...@@ -73,12 +73,14 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
73 * @param deviceId the device ID 73 * @param deviceId the device ID
74 * @param oldAppCookie the current group key 74 * @param oldAppCookie the current group key
75 * @param type update type 75 * @param type update type
76 - * @param newGroupDesc group description with updates 76 + * @param newBuckets group buckets for updates
77 + * @param newAppCookie optional new group key
77 */ 78 */
78 void updateGroupDescription(DeviceId deviceId, 79 void updateGroupDescription(DeviceId deviceId,
79 GroupKey oldAppCookie, 80 GroupKey oldAppCookie,
80 UpdateType type, 81 UpdateType type,
81 - GroupDescription newGroupDesc); 82 + GroupBuckets newBuckets,
83 + GroupKey newAppCookie);
82 84
83 /** 85 /**
84 * Triggers deleting the existing group entry. 86 * Triggers deleting the existing group entry.
...@@ -102,4 +104,43 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> { ...@@ -102,4 +104,43 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> {
102 * @param group group entry 104 * @param group group entry
103 */ 105 */
104 void removeGroupEntry(Group group); 106 void removeGroupEntry(Group group);
107 +
108 + /**
109 + * A group entry that is present in switch but not in the store.
110 + *
111 + * @param group group entry
112 + */
113 + void addOrUpdateExtraneousGroupEntry(Group group);
114 +
115 + /**
116 + * Remove the group entry from extraneous database.
117 + *
118 + * @param group group entry
119 + */
120 + void removeExtraneousGroupEntry(Group group);
121 +
122 + /**
123 + * Returns the extraneous groups associated with a device.
124 + *
125 + * @param deviceId the device ID
126 + *
127 + * @return the extraneous group entries
128 + */
129 + Iterable<Group> getExtraneousGroups(DeviceId deviceId);
130 +
131 + /**
132 + * Indicates the first group audit is completed.
133 + *
134 + * @param deviceId the device ID
135 + */
136 + void deviceInitialAuditCompleted(DeviceId deviceId);
137 +
138 + /**
139 + * Retrieves the initial group audit status for a device.
140 + *
141 + * @param deviceId the device ID
142 + *
143 + * @return initial group audit status
144 + */
145 + boolean deviceInitialAuditStatus(DeviceId deviceId);
105 } 146 }
......
...@@ -49,4 +49,10 @@ public interface StoredGroupEntry extends Group { ...@@ -49,4 +49,10 @@ public interface StoredGroupEntry extends Group {
49 */ 49 */
50 void setBytes(long bytes); 50 void setBytes(long bytes);
51 51
52 + /**
53 + * Sets number of flow rules or groups referencing this group entry.
54 + *
55 + * @param referenceCount reference count
56 + */
57 + void setReferenceCount(long referenceCount);
52 } 58 }
......
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.net.group.impl;
17 +
18 +import static org.slf4j.LoggerFactory.getLogger;
19 +
20 +import java.util.Arrays;
21 +import java.util.Collection;
22 +import java.util.Iterator;
23 +import java.util.Set;
24 +
25 +import org.apache.felix.scr.annotations.Activate;
26 +import org.apache.felix.scr.annotations.Component;
27 +import org.apache.felix.scr.annotations.Deactivate;
28 +import org.apache.felix.scr.annotations.Reference;
29 +import org.apache.felix.scr.annotations.ReferenceCardinality;
30 +import org.apache.felix.scr.annotations.Service;
31 +import org.onosproject.core.ApplicationId;
32 +import org.onosproject.event.AbstractListenerRegistry;
33 +import org.onosproject.event.EventDeliveryService;
34 +import org.onosproject.net.DeviceId;
35 +import org.onosproject.net.group.Group;
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.GroupListener;
41 +import org.onosproject.net.group.GroupOperation;
42 +import org.onosproject.net.group.GroupOperations;
43 +import org.onosproject.net.group.GroupProvider;
44 +import org.onosproject.net.group.GroupProviderRegistry;
45 +import org.onosproject.net.group.GroupProviderService;
46 +import org.onosproject.net.group.GroupService;
47 +import org.onosproject.net.group.GroupStore;
48 +import org.onosproject.net.group.GroupStore.UpdateType;
49 +import org.onosproject.net.group.GroupStoreDelegate;
50 +import org.onosproject.net.provider.AbstractProviderRegistry;
51 +import org.onosproject.net.provider.AbstractProviderService;
52 +import org.slf4j.Logger;
53 +
54 +import com.google.common.collect.Sets;
55 +
56 +/**
57 + * Provides implementation of the group service APIs.
58 + */
59 +@Component(immediate = true)
60 +@Service
61 +public class GroupManager
62 + extends AbstractProviderRegistry<GroupProvider, GroupProviderService>
63 + implements GroupService, GroupProviderRegistry {
64 +
65 + private final Logger log = getLogger(getClass());
66 +
67 + private final AbstractListenerRegistry<GroupEvent, GroupListener>
68 + listenerRegistry = new AbstractListenerRegistry<>();
69 + private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
70 +
71 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 + protected GroupStore store;
73 +
74 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 + protected EventDeliveryService eventDispatcher;
76 +
77 + @Activate
78 + public void activate() {
79 + store.setDelegate(delegate);
80 + eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
81 + log.info("Started");
82 + }
83 +
84 + @Deactivate
85 + public void deactivate() {
86 + store.unsetDelegate(delegate);
87 + eventDispatcher.removeSink(GroupEvent.class);
88 + log.info("Stopped");
89 + }
90 +
91 + /**
92 + * Create a group in the specified device with the provided parameters.
93 + *
94 + * @param groupDesc group creation parameters
95 + *
96 + */
97 + @Override
98 + public void addGroup(GroupDescription groupDesc) {
99 + store.storeGroupDescription(groupDesc);
100 + }
101 +
102 + /**
103 + * Return a group object associated to an application cookie.
104 + *
105 + * NOTE1: The presence of group object in the system does not
106 + * guarantee that the "group" is actually created in device.
107 + * GROUP_ADDED notification would confirm the creation of
108 + * this group in data plane.
109 + *
110 + * @param deviceId device identifier
111 + * @param appCookie application cookie to be used for lookup
112 + * @return group associated with the application cookie or
113 + * NULL if Group is not found for the provided cookie
114 + */
115 + @Override
116 + public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
117 + return store.getGroup(deviceId, appCookie);
118 + }
119 +
120 + /**
121 + * Append buckets to existing group. The caller can optionally
122 + * associate a new cookie during this updation. GROUP_UPDATED or
123 + * GROUP_UPDATE_FAILED notifications would be provided along with
124 + * cookie depending on the result of the operation on the device.
125 + *
126 + * @param deviceId device identifier
127 + * @param oldCookie cookie to be used to retrieve the existing group
128 + * @param buckets immutable list of group bucket to be added
129 + * @param newCookie immutable cookie to be used post update operation
130 + * @param appId Application Id
131 + */
132 + @Override
133 + public void addBucketsToGroup(DeviceId deviceId,
134 + GroupKey oldCookie,
135 + GroupBuckets buckets,
136 + GroupKey newCookie,
137 + ApplicationId appId) {
138 + store.updateGroupDescription(deviceId,
139 + oldCookie,
140 + UpdateType.ADD,
141 + buckets,
142 + newCookie);
143 + }
144 +
145 + /**
146 + * Remove buckets from existing group. The caller can optionally
147 + * associate a new cookie during this updation. GROUP_UPDATED or
148 + * GROUP_UPDATE_FAILED notifications would be provided along with
149 + * cookie depending on the result of the operation on the device.
150 + *
151 + * @param deviceId device identifier
152 + * @param oldCookie cookie to be used to retrieve the existing group
153 + * @param buckets immutable list of group bucket to be removed
154 + * @param newCookie immutable cookie to be used post update operation
155 + * @param appId Application Id
156 + */
157 + @Override
158 + public void removeBucketsFromGroup(DeviceId deviceId,
159 + GroupKey oldCookie,
160 + GroupBuckets buckets,
161 + GroupKey newCookie,
162 + ApplicationId appId) {
163 + store.updateGroupDescription(deviceId,
164 + oldCookie,
165 + UpdateType.REMOVE,
166 + buckets,
167 + newCookie);
168 + }
169 +
170 + /**
171 + * Delete a group associated to an application cookie.
172 + * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
173 + * provided along with cookie depending on the result of the
174 + * operation on the device.
175 + *
176 + * @param deviceId device identifier
177 + * @param appCookie application cookie to be used for lookup
178 + * @param appId Application Id
179 + */
180 + @Override
181 + public void removeGroup(DeviceId deviceId,
182 + GroupKey appCookie,
183 + ApplicationId appId) {
184 + store.deleteGroupDescription(deviceId, appCookie);
185 + }
186 +
187 + /**
188 + * Retrieve all groups created by an application in the specified device
189 + * as seen by current controller instance.
190 + *
191 + * @param deviceId device identifier
192 + * @param appId application id
193 + * @return collection of immutable group objects created by the application
194 + */
195 + @Override
196 + public Iterable<Group> getGroups(DeviceId deviceId,
197 + ApplicationId appId) {
198 + return store.getGroups(deviceId);
199 + }
200 +
201 + /**
202 + * Adds the specified group listener.
203 + *
204 + * @param listener group listener
205 + */
206 + @Override
207 + public void addListener(GroupListener listener) {
208 + listenerRegistry.addListener(listener);
209 + }
210 +
211 + /**
212 + * Removes the specified group listener.
213 + *
214 + * @param listener group listener
215 + */
216 + @Override
217 + public void removeListener(GroupListener listener) {
218 + listenerRegistry.removeListener(listener);
219 + }
220 +
221 + @Override
222 + protected GroupProviderService createProviderService(GroupProvider provider) {
223 + return new InternalGroupProviderService(provider);
224 + }
225 +
226 + private class InternalGroupStoreDelegate implements GroupStoreDelegate {
227 + @Override
228 + public void notify(GroupEvent event) {
229 + final Group group = event.subject();
230 + GroupProvider groupProvider =
231 + getProvider(group.deviceId());
232 + GroupOperations groupOps = null;
233 + switch (event.type()) {
234 + case GROUP_ADD_REQUESTED:
235 + GroupOperation groupAddOp = GroupOperation.
236 + createAddGroupOperation(group.id(),
237 + group.type(),
238 + group.buckets());
239 + groupOps = new GroupOperations(
240 + Arrays.asList(groupAddOp));
241 + groupProvider.performGroupOperation(group.deviceId(), groupOps);
242 + break;
243 +
244 + case GROUP_UPDATE_REQUESTED:
245 + GroupOperation groupModifyOp = GroupOperation.
246 + createModifyGroupOperation(group.id(),
247 + group.type(),
248 + group.buckets());
249 + groupOps = new GroupOperations(
250 + Arrays.asList(groupModifyOp));
251 + groupProvider.performGroupOperation(group.deviceId(), groupOps);
252 + break;
253 +
254 + case GROUP_REMOVE_REQUESTED:
255 + GroupOperation groupDeleteOp = GroupOperation.
256 + createDeleteGroupOperation(group.id(),
257 + group.type());
258 + groupOps = new GroupOperations(
259 + Arrays.asList(groupDeleteOp));
260 + groupProvider.performGroupOperation(group.deviceId(), groupOps);
261 + break;
262 +
263 + case GROUP_ADDED:
264 + case GROUP_UPDATED:
265 + case GROUP_REMOVED:
266 + eventDispatcher.post(event);
267 + break;
268 +
269 + default:
270 + break;
271 + }
272 + }
273 + }
274 +
275 + private class InternalGroupProviderService
276 + extends AbstractProviderService<GroupProvider>
277 + implements GroupProviderService {
278 +
279 + protected InternalGroupProviderService(GroupProvider provider) {
280 + super(provider);
281 + }
282 +
283 + @Override
284 + public void groupOperationFailed(GroupOperation operation) {
285 + // TODO Auto-generated method stub
286 +
287 + }
288 +
289 + private void groupMissing(Group group) {
290 + checkValidity();
291 + GroupProvider gp = getProvider(group.deviceId());
292 + switch (group.state()) {
293 + case PENDING_DELETE:
294 + store.removeGroupEntry(group);
295 + break;
296 + case ADDED:
297 + case PENDING_ADD:
298 + GroupOperation groupAddOp = GroupOperation.
299 + createAddGroupOperation(group.id(),
300 + group.type(),
301 + group.buckets());
302 + GroupOperations groupOps = new GroupOperations(
303 + Arrays.asList(groupAddOp));
304 + gp.performGroupOperation(group.deviceId(), groupOps);
305 + break;
306 + default:
307 + log.debug("Group {} has not been installed.", group);
308 + break;
309 + }
310 + }
311 +
312 +
313 + private void extraneousGroup(Group group) {
314 + log.debug("Group {} is on switch but not in store.", group);
315 + checkValidity();
316 + store.addOrUpdateExtraneousGroupEntry(group);
317 + }
318 +
319 + private void groupAdded(Group group) {
320 + checkValidity();
321 +
322 + log.trace("Group {}", group);
323 + store.addOrUpdateGroupEntry(group);
324 + }
325 +
326 + @Override
327 + public void pushGroupMetrics(DeviceId deviceId,
328 + Collection<Group> groupEntries) {
329 + boolean deviceInitialAuditStatus =
330 + store.deviceInitialAuditStatus(deviceId);
331 + Set<Group> southboundGroupEntries =
332 + Sets.newHashSet(groupEntries);
333 + Set<Group> storedGroupEntries =
334 + Sets.newHashSet(store.getGroups(deviceId));
335 + Set<Group> extraneousStoredEntries =
336 + Sets.newHashSet(store.getExtraneousGroups(deviceId));
337 +
338 + for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
339 + Group group = it.next();
340 + if (storedGroupEntries.remove(group)) {
341 + // we both have the group, let's update some info then.
342 + groupAdded(group);
343 + it.remove();
344 + }
345 + }
346 + for (Group group : southboundGroupEntries) {
347 + // there are groups in the switch that aren't in the store
348 + extraneousStoredEntries.remove(group);
349 + extraneousGroup(group);
350 + }
351 + for (Group group : storedGroupEntries) {
352 + // there are groups in the store that aren't in the switch
353 + groupMissing(group);
354 + }
355 + for (Group group : extraneousStoredEntries) {
356 + // there are groups in the extraneous store that
357 + // aren't in the switch
358 + store.removeExtraneousGroupEntry(group);
359 + }
360 +
361 + if (!deviceInitialAuditStatus) {
362 + store.deviceInitialAuditCompleted(deviceId);
363 + }
364 + }
365 + }
366 +}
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 +/**
18 + * Core subsystem for group state.
19 + */
20 +package org.onosproject.net.group.impl;
...\ 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.net.group.impl;
17 +
18 +import static org.junit.Assert.assertEquals;
19 +import static org.junit.Assert.assertFalse;
20 +import static org.junit.Assert.assertNotEquals;
21 +import static org.junit.Assert.assertTrue;
22 +
23 +import java.util.ArrayList;
24 +import java.util.Arrays;
25 +import java.util.Collections;
26 +import java.util.List;
27 +
28 +import org.junit.After;
29 +import org.junit.Before;
30 +import org.junit.Test;
31 +import org.onlab.packet.MacAddress;
32 +import org.onosproject.core.ApplicationId;
33 +import org.onosproject.core.DefaultApplicationId;
34 +import org.onosproject.core.DefaultGroupId;
35 +import org.onosproject.core.GroupId;
36 +import org.onosproject.event.impl.TestEventDispatcher;
37 +import org.onosproject.net.DeviceId;
38 +import org.onosproject.net.PortNumber;
39 +import org.onosproject.net.flow.DefaultTrafficTreatment;
40 +import org.onosproject.net.flow.TrafficTreatment;
41 +import org.onosproject.net.group.DefaultGroup;
42 +import org.onosproject.net.group.DefaultGroupBucket;
43 +import org.onosproject.net.group.DefaultGroupDescription;
44 +import org.onosproject.net.group.Group;
45 +import org.onosproject.net.group.GroupBucket;
46 +import org.onosproject.net.group.GroupBuckets;
47 +import org.onosproject.net.group.GroupDescription;
48 +import org.onosproject.net.group.GroupEvent;
49 +import org.onosproject.net.group.GroupKey;
50 +import org.onosproject.net.group.GroupListener;
51 +import org.onosproject.net.group.GroupOperation;
52 +import org.onosproject.net.group.GroupOperations;
53 +import org.onosproject.net.group.GroupProvider;
54 +import org.onosproject.net.group.GroupProviderRegistry;
55 +import org.onosproject.net.group.GroupProviderService;
56 +import org.onosproject.net.group.GroupService;
57 +import org.onosproject.net.group.StoredGroupEntry;
58 +import org.onosproject.net.provider.AbstractProvider;
59 +import org.onosproject.net.provider.ProviderId;
60 +import org.onosproject.store.trivial.impl.SimpleGroupStore;
61 +
62 +import com.google.common.collect.Iterables;
63 +
64 +/**
65 + * Test codifying the group service & group provider service contracts.
66 + */
67 +public class GroupManagerTest {
68 +
69 + private static final ProviderId PID = new ProviderId("of", "groupfoo");
70 + private static final DeviceId DID = DeviceId.deviceId("of:001");
71 +
72 + private GroupManager mgr;
73 + private GroupService groupService;
74 + private GroupProviderRegistry providerRegistry;
75 + private TestGroupListener internalListener = new TestGroupListener();
76 + private GroupListener listener = internalListener;
77 + private TestGroupProvider internalProvider;
78 + private GroupProvider provider;
79 + private GroupProviderService providerService;
80 + private ApplicationId appId;
81 +
82 + @Before
83 + public void setUp() {
84 + mgr = new GroupManager();
85 + groupService = mgr;
86 + mgr.store = new SimpleGroupStore();
87 + mgr.eventDispatcher = new TestEventDispatcher();
88 + providerRegistry = mgr;
89 +
90 + mgr.activate();
91 + mgr.addListener(listener);
92 +
93 + internalProvider = new TestGroupProvider(PID);
94 + provider = internalProvider;
95 + providerService = providerRegistry.register(provider);
96 + appId = new DefaultApplicationId(2, "org.groupmanager.test");
97 + assertTrue("provider should be registered",
98 + providerRegistry.getProviders().contains(provider.id()));
99 + }
100 +
101 + @After
102 + public void tearDown() {
103 + providerRegistry.unregister(provider);
104 + assertFalse("provider should not be registered",
105 + providerRegistry.getProviders().contains(provider.id()));
106 + mgr.removeListener(listener);
107 + mgr.deactivate();
108 + mgr.eventDispatcher = null;
109 + }
110 +
111 + private class TestGroupKey implements GroupKey {
112 + private String groupId;
113 +
114 + public TestGroupKey(String id) {
115 + this.groupId = id;
116 + }
117 +
118 + public String id() {
119 + return this.groupId;
120 + }
121 +
122 + @Override
123 + public int hashCode() {
124 + return groupId.hashCode();
125 + }
126 +
127 + @Override
128 + public boolean equals(Object obj) {
129 + if (obj instanceof TestGroupKey) {
130 + return this.groupId.equals(((TestGroupKey) obj).id());
131 + }
132 + return false;
133 + }
134 + }
135 +
136 + /**
137 + * Tests group service north bound and south bound interfaces.
138 + * The following operations are tested:
139 + * a)Tests group creation before the device group AUDIT completes
140 + * b)Tests initial device group AUDIT process
141 + * c)Tests deletion process of any extraneous groups
142 + * d)Tests execution of any pending group creation requests
143 + * after the device group AUDIT completes
144 + * e)Tests re-apply process of any missing groups
145 + * f)Tests event notifications after receiving confirmation for
146 + * any operations from data plane
147 + * g)Tests group bucket modifications (additions and deletions)
148 + * h)Tests group deletion
149 + */
150 + @Test
151 + public void testGroupService() {
152 + PortNumber[] ports1 = {PortNumber.portNumber(31),
153 + PortNumber.portNumber(32)};
154 + PortNumber[] ports2 = {PortNumber.portNumber(41),
155 + PortNumber.portNumber(42)};
156 + // Test Group creation before AUDIT process
157 + TestGroupKey key = new TestGroupKey("group1BeforeAudit");
158 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
159 + List<PortNumber> outPorts = new ArrayList<PortNumber>();
160 + outPorts.addAll(Arrays.asList(ports1));
161 + outPorts.addAll(Arrays.asList(ports2));
162 + for (PortNumber portNumber: outPorts) {
163 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
164 + tBuilder.setOutput(portNumber)
165 + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
166 + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
167 + .pushMpls()
168 + .setMpls(106);
169 + buckets.add(DefaultGroupBucket.createSelectGroupBucket(
170 + tBuilder.build()));
171 + }
172 + GroupBuckets groupBuckets = new GroupBuckets(buckets);
173 + GroupDescription newGroupDesc = new DefaultGroupDescription(DID,
174 + Group.Type.SELECT,
175 + groupBuckets,
176 + key,
177 + appId);
178 + groupService.addGroup(newGroupDesc);
179 + internalProvider.validate(DID, null);
180 + assertEquals(null, groupService.getGroup(DID, key));
181 + assertEquals(0, Iterables.size(groupService.getGroups(DID, appId)));
182 +
183 + // Test initial group audit process
184 + GroupId gId1 = new DefaultGroupId(1);
185 + Group group1 = createSouthboundGroupEntry(gId1,
186 + Arrays.asList(ports1),
187 + 0);
188 + GroupId gId2 = new DefaultGroupId(2);
189 + // Non zero reference count will make the group manager to queue
190 + // the extraneous groups until reference count is zero.
191 + Group group2 = createSouthboundGroupEntry(gId2,
192 + Arrays.asList(ports2),
193 + 2);
194 + List<Group> groupEntries = Arrays.asList(group1, group2);
195 + providerService.pushGroupMetrics(DID, groupEntries);
196 + // First group metrics would trigger the device audit completion
197 + // post which all pending group requests are also executed.
198 + Group createdGroup = groupService.getGroup(DID, key);
199 + int createdGroupId = createdGroup.id().id();
200 + assertNotEquals(gId1.id(), createdGroupId);
201 + assertNotEquals(gId2.id(), createdGroupId);
202 + List<GroupOperation> expectedGroupOps = Arrays.asList(
203 + GroupOperation.createDeleteGroupOperation(gId1,
204 + Group.Type.SELECT),
205 + GroupOperation.createAddGroupOperation(
206 + createdGroup.id(),
207 + Group.Type.SELECT,
208 + groupBuckets));
209 + internalProvider.validate(DID, expectedGroupOps);
210 +
211 + group1 = createSouthboundGroupEntry(gId1,
212 + Arrays.asList(ports1),
213 + 0);
214 + group2 = createSouthboundGroupEntry(gId2,
215 + Arrays.asList(ports2),
216 + 0);
217 + groupEntries = Arrays.asList(group1, group2);
218 + providerService.pushGroupMetrics(DID, groupEntries);
219 + expectedGroupOps = Arrays.asList(
220 + GroupOperation.createDeleteGroupOperation(gId1,
221 + Group.Type.SELECT),
222 + GroupOperation.createDeleteGroupOperation(gId2,
223 + Group.Type.SELECT),
224 + GroupOperation.createAddGroupOperation(createdGroup.id(),
225 + Group.Type.SELECT,
226 + groupBuckets));
227 + internalProvider.validate(DID, expectedGroupOps);
228 +
229 + createdGroup = new DefaultGroup(createdGroup.id(),
230 + DID,
231 + Group.Type.SELECT,
232 + groupBuckets);
233 + groupEntries = Arrays.asList(createdGroup);
234 + providerService.pushGroupMetrics(DID, groupEntries);
235 + internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
236 +
237 + // Test group add bucket operations
238 + TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
239 + PortNumber[] addPorts = {PortNumber.portNumber(51),
240 + PortNumber.portNumber(52)};
241 + outPorts.clear();
242 + outPorts.addAll(Arrays.asList(addPorts));
243 + List<GroupBucket> addBuckets = new ArrayList<GroupBucket>();
244 + for (PortNumber portNumber: outPorts) {
245 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
246 + tBuilder.setOutput(portNumber)
247 + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
248 + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
249 + .pushMpls()
250 + .setMpls(106);
251 + addBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
252 + tBuilder.build()));
253 + buckets.add(DefaultGroupBucket.createSelectGroupBucket(
254 + tBuilder.build()));
255 + }
256 + GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
257 + groupService.addBucketsToGroup(DID,
258 + key,
259 + groupAddBuckets,
260 + addKey,
261 + appId);
262 + GroupBuckets updatedBuckets = new GroupBuckets(buckets);
263 + expectedGroupOps = Arrays.asList(
264 + GroupOperation.createModifyGroupOperation(createdGroup.id(),
265 + Group.Type.SELECT,
266 + updatedBuckets));
267 + internalProvider.validate(DID, expectedGroupOps);
268 + Group existingGroup = groupService.getGroup(DID, addKey);
269 + groupEntries = Arrays.asList(existingGroup);
270 + providerService.pushGroupMetrics(DID, groupEntries);
271 + internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
272 +
273 + // Test group remove bucket operations
274 + TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
275 + PortNumber[] removePorts = {PortNumber.portNumber(31),
276 + PortNumber.portNumber(32)};
277 + outPorts.clear();
278 + outPorts.addAll(Arrays.asList(removePorts));
279 + List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>();
280 + for (PortNumber portNumber: outPorts) {
281 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
282 + tBuilder.setOutput(portNumber)
283 + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
284 + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
285 + .pushMpls()
286 + .setMpls(106);
287 + removeBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
288 + tBuilder.build()));
289 + buckets.remove(DefaultGroupBucket.createSelectGroupBucket(
290 + tBuilder.build()));
291 + }
292 + GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
293 + groupService.removeBucketsFromGroup(DID,
294 + addKey,
295 + groupRemoveBuckets,
296 + removeKey,
297 + appId);
298 + updatedBuckets = new GroupBuckets(buckets);
299 + expectedGroupOps = Arrays.asList(
300 + GroupOperation.createModifyGroupOperation(createdGroup.id(),
301 + Group.Type.SELECT,
302 + updatedBuckets));
303 + internalProvider.validate(DID, expectedGroupOps);
304 + existingGroup = groupService.getGroup(DID, removeKey);
305 + groupEntries = Arrays.asList(existingGroup);
306 + providerService.pushGroupMetrics(DID, groupEntries);
307 + internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
308 +
309 + // Test group remove operations
310 + groupService.removeGroup(DID, removeKey, appId);
311 + expectedGroupOps = Arrays.asList(
312 + GroupOperation.createDeleteGroupOperation(createdGroup.id(),
313 + Group.Type.SELECT));
314 + internalProvider.validate(DID, expectedGroupOps);
315 + groupEntries = Collections.emptyList();
316 + providerService.pushGroupMetrics(DID, groupEntries);
317 + internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
318 + }
319 +
320 + private Group createSouthboundGroupEntry(GroupId gId,
321 + List<PortNumber> ports,
322 + long referenceCount) {
323 + List<PortNumber> outPorts = new ArrayList<PortNumber>();
324 + outPorts.addAll(ports);
325 +
326 + List<GroupBucket> buckets = new ArrayList<GroupBucket>();
327 + for (PortNumber portNumber: outPorts) {
328 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
329 + tBuilder.setOutput(portNumber)
330 + .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
331 + .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
332 + .pushMpls()
333 + .setMpls(106);
334 + buckets.add(DefaultGroupBucket.createSelectGroupBucket(
335 + tBuilder.build()));
336 + }
337 + GroupBuckets groupBuckets = new GroupBuckets(buckets);
338 + StoredGroupEntry group = new DefaultGroup(
339 + gId, DID, Group.Type.SELECT, groupBuckets);
340 + group.setReferenceCount(referenceCount);
341 + return group;
342 + }
343 +
344 + private static class TestGroupListener implements GroupListener {
345 + final List<GroupEvent> events = new ArrayList<>();
346 +
347 + @Override
348 + public void event(GroupEvent event) {
349 + events.add(event);
350 + }
351 +
352 + public void validateEvent(List<GroupEvent.Type> expectedEvents) {
353 + int i = 0;
354 + System.err.println("events :" + events);
355 + for (GroupEvent e : events) {
356 + assertEquals("unexpected event", expectedEvents.get(i), e.type());
357 + i++;
358 + }
359 + assertEquals("mispredicted number of events",
360 + expectedEvents.size(), events.size());
361 + events.clear();
362 + }
363 + }
364 +
365 + private class TestGroupProvider
366 + extends AbstractProvider implements GroupProvider {
367 + DeviceId lastDeviceId;
368 + List<GroupOperation> groupOperations = new ArrayList<GroupOperation>();
369 +
370 + protected TestGroupProvider(ProviderId id) {
371 + super(id);
372 + }
373 +
374 + @Override
375 + public void performGroupOperation(DeviceId deviceId,
376 + GroupOperations groupOps) {
377 + lastDeviceId = deviceId;
378 + groupOperations.addAll(groupOps.operations());
379 + }
380 +
381 + public void validate(DeviceId expectedDeviceId,
382 + List<GroupOperation> expectedGroupOps) {
383 + if (expectedGroupOps == null) {
384 + assertTrue("events generated", groupOperations.isEmpty());
385 + return;
386 + }
387 +
388 + assertEquals(lastDeviceId, expectedDeviceId);
389 + assertTrue((this.groupOperations.containsAll(expectedGroupOps) &&
390 + expectedGroupOps.containsAll(groupOperations)));
391 +
392 + groupOperations.clear();
393 + lastDeviceId = null;
394 + }
395 +
396 + }
397 +
398 +}
399 +
400 +
...@@ -19,6 +19,7 @@ import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsent ...@@ -19,6 +19,7 @@ import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsent
19 import static org.slf4j.LoggerFactory.getLogger; 19 import static org.slf4j.LoggerFactory.getLogger;
20 20
21 import java.util.ArrayList; 21 import java.util.ArrayList;
22 +import java.util.HashMap;
22 import java.util.List; 23 import java.util.List;
23 import java.util.concurrent.ConcurrentHashMap; 24 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap; 25 import java.util.concurrent.ConcurrentMap;
...@@ -62,11 +63,21 @@ public class SimpleGroupStore ...@@ -62,11 +63,21 @@ public class SimpleGroupStore
62 63
63 private final Logger log = getLogger(getClass()); 64 private final Logger log = getLogger(getClass());
64 65
66 + private final int dummyId = 0xffffffff;
67 + private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
68 +
65 // inner Map is per device group table 69 // inner Map is per device group table
66 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>> 70 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
67 groupEntriesByKey = new ConcurrentHashMap<>(); 71 groupEntriesByKey = new ConcurrentHashMap<>();
68 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>> 72 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
69 groupEntriesById = new ConcurrentHashMap<>(); 73 groupEntriesById = new ConcurrentHashMap<>();
74 + private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
75 + pendingGroupEntriesByKey = new ConcurrentHashMap<>();
76 + private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
77 + extraneousGroupEntriesById = new ConcurrentHashMap<>();
78 +
79 + private final HashMap<DeviceId, Boolean> deviceAuditStatus =
80 + new HashMap<DeviceId, Boolean>();
70 81
71 private final AtomicInteger groupIdGen = new AtomicInteger(); 82 private final AtomicInteger groupIdGen = new AtomicInteger();
72 83
...@@ -82,14 +93,26 @@ public class SimpleGroupStore ...@@ -82,14 +93,26 @@ public class SimpleGroupStore
82 log.info("Stopped"); 93 log.info("Stopped");
83 } 94 }
84 95
85 - private static NewConcurrentHashMap<GroupKey, StoredGroupEntry> lazyEmptyGroupKeyTable() { 96 + private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
97 + lazyEmptyGroupKeyTable() {
86 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded(); 98 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
87 } 99 }
88 100
89 - private static NewConcurrentHashMap<GroupId, StoredGroupEntry> lazyEmptyGroupIdTable() { 101 + private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
102 + lazyEmptyGroupIdTable() {
90 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded(); 103 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
91 } 104 }
92 105
106 + private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
107 + lazyEmptyPendingGroupKeyTable() {
108 + return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
109 + }
110 +
111 + private static NewConcurrentHashMap<GroupId, Group>
112 + lazyEmptyExtraneousGroupIdTable() {
113 + return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
114 + }
115 +
93 /** 116 /**
94 * Returns the group key table for specified device. 117 * Returns the group key table for specified device.
95 * 118 *
...@@ -113,6 +136,31 @@ public class SimpleGroupStore ...@@ -113,6 +136,31 @@ public class SimpleGroupStore
113 } 136 }
114 137
115 /** 138 /**
139 + * Returns the pending group key table for specified device.
140 + *
141 + * @param deviceId identifier of the device
142 + * @return Map representing group key table of given device.
143 + */
144 + private ConcurrentMap<GroupKey, StoredGroupEntry>
145 + getPendingGroupKeyTable(DeviceId deviceId) {
146 + return createIfAbsentUnchecked(pendingGroupEntriesByKey,
147 + deviceId, lazyEmptyPendingGroupKeyTable());
148 + }
149 +
150 + /**
151 + * Returns the extraneous group id table for specified device.
152 + *
153 + * @param deviceId identifier of the device
154 + * @return Map representing group key table of given device.
155 + */
156 + private ConcurrentMap<GroupId, Group>
157 + getExtraneousGroupIdTable(DeviceId deviceId) {
158 + return createIfAbsentUnchecked(extraneousGroupEntriesById,
159 + deviceId,
160 + lazyEmptyExtraneousGroupIdTable());
161 + }
162 +
163 + /**
116 * Returns the number of groups for the specified device in the store. 164 * Returns the number of groups for the specified device in the store.
117 * 165 *
118 * @return number of groups for the specified device 166 * @return number of groups for the specified device
...@@ -133,20 +181,16 @@ public class SimpleGroupStore ...@@ -133,20 +181,16 @@ public class SimpleGroupStore
133 @Override 181 @Override
134 public Iterable<Group> getGroups(DeviceId deviceId) { 182 public Iterable<Group> getGroups(DeviceId deviceId) {
135 // flatten and make iterator unmodifiable 183 // flatten and make iterator unmodifiable
136 - if (groupEntriesByKey.get(deviceId) != null) { 184 + return FluentIterable.from(getGroupKeyTable(deviceId).values())
137 - return FluentIterable.from(groupEntriesByKey.get(deviceId).values()) 185 + .transform(
138 - .transform( 186 + new Function<StoredGroupEntry, Group>() {
139 - new Function<StoredGroupEntry, Group>() { 187 +
140 - 188 + @Override
141 - @Override 189 + public Group apply(
142 - public Group apply( 190 + StoredGroupEntry input) {
143 - StoredGroupEntry input) { 191 + return input;
144 - return input; 192 + }
145 - } 193 + });
146 - });
147 - } else {
148 - return null;
149 - }
150 } 194 }
151 195
152 /** 196 /**
...@@ -164,6 +208,30 @@ public class SimpleGroupStore ...@@ -164,6 +208,30 @@ public class SimpleGroupStore
164 null; 208 null;
165 } 209 }
166 210
211 + private int getFreeGroupIdValue(DeviceId deviceId) {
212 + int freeId = groupIdGen.incrementAndGet();
213 +
214 + while (true) {
215 + Group existing = (
216 + groupEntriesById.get(deviceId) != null) ?
217 + groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
218 + null;
219 + if (existing == null) {
220 + existing = (
221 + extraneousGroupEntriesById.get(deviceId) != null) ?
222 + extraneousGroupEntriesById.get(deviceId).
223 + get(new DefaultGroupId(freeId)) :
224 + null;
225 + }
226 + if (existing != null) {
227 + freeId = groupIdGen.incrementAndGet();
228 + } else {
229 + break;
230 + }
231 + }
232 + return freeId;
233 + }
234 +
167 /** 235 /**
168 * Stores a new group entry using the information from group description. 236 * Stores a new group entry using the information from group description.
169 * 237 *
...@@ -171,16 +239,32 @@ public class SimpleGroupStore ...@@ -171,16 +239,32 @@ public class SimpleGroupStore
171 */ 239 */
172 @Override 240 @Override
173 public void storeGroupDescription(GroupDescription groupDesc) { 241 public void storeGroupDescription(GroupDescription groupDesc) {
174 - /* Check if a group is existing with the same key */ 242 + // Check if a group is existing with the same key
175 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) { 243 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
176 return; 244 return;
177 } 245 }
178 246
179 - /* Get a new group identifier */ 247 + if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
180 - GroupId id = new DefaultGroupId(groupIdGen.incrementAndGet()); 248 + // Device group audit has not completed yet
181 - /* Create a group entry object */ 249 + // Add this group description to pending group key table
250 + // Create a group entry object with Dummy Group ID
251 + StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
252 + group.setState(GroupState.WAITING_AUDIT_COMPLETE);
253 + ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
254 + getPendingGroupKeyTable(groupDesc.deviceId());
255 + pendingKeyTable.put(groupDesc.appCookie(), group);
256 + return;
257 + }
258 +
259 + storeGroupDescriptionInternal(groupDesc);
260 + }
261 +
262 + private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
263 + // Get a new group identifier
264 + GroupId id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
265 + // Create a group entry object
182 StoredGroupEntry group = new DefaultGroup(id, groupDesc); 266 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
183 - /* Insert the newly created group entry into concurrent key and id maps */ 267 + // Insert the newly created group entry into concurrent key and id maps
184 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable = 268 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
185 getGroupKeyTable(groupDesc.deviceId()); 269 getGroupKeyTable(groupDesc.deviceId());
186 keyTable.put(groupDesc.appCookie(), group); 270 keyTable.put(groupDesc.appCookie(), group);
...@@ -198,14 +282,16 @@ public class SimpleGroupStore ...@@ -198,14 +282,16 @@ public class SimpleGroupStore
198 * @param deviceId the device ID 282 * @param deviceId the device ID
199 * @param oldAppCookie the current group key 283 * @param oldAppCookie the current group key
200 * @param type update type 284 * @param type update type
201 - * @param newGroupDesc group description with updates 285 + * @param newBuckets group buckets for updates
286 + * @param newAppCookie optional new group key
202 */ 287 */
203 @Override 288 @Override
204 public void updateGroupDescription(DeviceId deviceId, 289 public void updateGroupDescription(DeviceId deviceId,
205 GroupKey oldAppCookie, 290 GroupKey oldAppCookie,
206 UpdateType type, 291 UpdateType type,
207 - GroupDescription newGroupDesc) { 292 + GroupBuckets newBuckets,
208 - /* Check if a group is existing with the provided key */ 293 + GroupKey newAppCookie) {
294 + // Check if a group is existing with the provided key
209 Group oldGroup = getGroup(deviceId, oldAppCookie); 295 Group oldGroup = getGroup(deviceId, oldAppCookie);
210 if (oldGroup == null) { 296 if (oldGroup == null) {
211 return; 297 return;
...@@ -213,15 +299,16 @@ public class SimpleGroupStore ...@@ -213,15 +299,16 @@ public class SimpleGroupStore
213 299
214 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup, 300 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
215 type, 301 type,
216 - newGroupDesc.buckets()); 302 + newBuckets);
217 if (newBucketList != null) { 303 if (newBucketList != null) {
218 - /* Create a new group object from the old group */ 304 + // Create a new group object from the old group
219 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList); 305 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
306 + GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
220 GroupDescription updatedGroupDesc = new DefaultGroupDescription( 307 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
221 oldGroup.deviceId(), 308 oldGroup.deviceId(),
222 oldGroup.type(), 309 oldGroup.type(),
223 updatedBuckets, 310 updatedBuckets,
224 - newGroupDesc.appCookie(), 311 + newCookie,
225 oldGroup.appId()); 312 oldGroup.appId());
226 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(), 313 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
227 updatedGroupDesc); 314 updatedGroupDesc);
...@@ -229,9 +316,7 @@ public class SimpleGroupStore ...@@ -229,9 +316,7 @@ public class SimpleGroupStore
229 newGroup.setLife(oldGroup.life()); 316 newGroup.setLife(oldGroup.life());
230 newGroup.setPackets(oldGroup.packets()); 317 newGroup.setPackets(oldGroup.packets());
231 newGroup.setBytes(oldGroup.bytes()); 318 newGroup.setBytes(oldGroup.bytes());
232 - /* Remove the old entry from maps and add new entry 319 + // Remove the old entry from maps and add new entry using new key
233 - * using new key
234 - */
235 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable = 320 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
236 getGroupKeyTable(oldGroup.deviceId()); 321 getGroupKeyTable(oldGroup.deviceId());
237 ConcurrentMap<GroupId, StoredGroupEntry> idTable = 322 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
...@@ -253,9 +338,8 @@ public class SimpleGroupStore ...@@ -253,9 +338,8 @@ public class SimpleGroupStore
253 boolean groupDescUpdated = false; 338 boolean groupDescUpdated = false;
254 339
255 if (type == UpdateType.ADD) { 340 if (type == UpdateType.ADD) {
256 - /* Check if the any of the new buckets are part of the 341 + // Check if the any of the new buckets are part of
257 - * old bucket list 342 + // the old bucket list
258 - */
259 for (GroupBucket addBucket:buckets.buckets()) { 343 for (GroupBucket addBucket:buckets.buckets()) {
260 if (!newBucketList.contains(addBucket)) { 344 if (!newBucketList.contains(addBucket)) {
261 newBucketList.add(addBucket); 345 newBucketList.add(addBucket);
...@@ -263,9 +347,8 @@ public class SimpleGroupStore ...@@ -263,9 +347,8 @@ public class SimpleGroupStore
263 } 347 }
264 } 348 }
265 } else if (type == UpdateType.REMOVE) { 349 } else if (type == UpdateType.REMOVE) {
266 - /* Check if the to be removed buckets are part of the 350 + // Check if the to be removed buckets are part of the
267 - * old bucket list 351 + // old bucket list
268 - */
269 for (GroupBucket removeBucket:buckets.buckets()) { 352 for (GroupBucket removeBucket:buckets.buckets()) {
270 if (newBucketList.contains(removeBucket)) { 353 if (newBucketList.contains(removeBucket)) {
271 newBucketList.remove(removeBucket); 354 newBucketList.remove(removeBucket);
...@@ -290,7 +373,7 @@ public class SimpleGroupStore ...@@ -290,7 +373,7 @@ public class SimpleGroupStore
290 @Override 373 @Override
291 public void deleteGroupDescription(DeviceId deviceId, 374 public void deleteGroupDescription(DeviceId deviceId,
292 GroupKey appCookie) { 375 GroupKey appCookie) {
293 - /* Check if a group is existing with the provided key */ 376 + // Check if a group is existing with the provided key
294 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ? 377 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
295 groupEntriesByKey.get(deviceId).get(appCookie) : 378 groupEntriesByKey.get(deviceId).get(appCookie) :
296 null; 379 null;
...@@ -362,4 +445,56 @@ public class SimpleGroupStore ...@@ -362,4 +445,56 @@ public class SimpleGroupStore
362 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing)); 445 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
363 } 446 }
364 } 447 }
448 +
449 + @Override
450 + public void deviceInitialAuditCompleted(DeviceId deviceId) {
451 + synchronized (deviceAuditStatus) {
452 + deviceAuditStatus.putIfAbsent(deviceId, true);
453 + // Execute all pending group requests
454 + ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
455 + getPendingGroupKeyTable(deviceId);
456 + for (Group group:pendingGroupRequests.values()) {
457 + GroupDescription tmp = new DefaultGroupDescription(
458 + group.deviceId(),
459 + group.type(),
460 + group.buckets(),
461 + group.appCookie(),
462 + group.appId());
463 + storeGroupDescriptionInternal(tmp);
464 + }
465 + getPendingGroupKeyTable(deviceId).clear();
466 + }
467 + }
468 +
469 + @Override
470 + public boolean deviceInitialAuditStatus(DeviceId deviceId) {
471 + synchronized (deviceAuditStatus) {
472 + return (deviceAuditStatus.get(deviceId) != null) ? true : false;
473 + }
474 + }
475 +
476 + @Override
477 + public void addOrUpdateExtraneousGroupEntry(Group group) {
478 + ConcurrentMap<GroupId, Group> extraneousIdTable =
479 + getExtraneousGroupIdTable(group.deviceId());
480 + extraneousIdTable.put(group.id(), group);
481 + // Check the reference counter
482 + if (group.referenceCount() == 0) {
483 + notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
484 + }
485 + }
486 +
487 + @Override
488 + public void removeExtraneousGroupEntry(Group group) {
489 + ConcurrentMap<GroupId, Group> extraneousIdTable =
490 + getExtraneousGroupIdTable(group.deviceId());
491 + extraneousIdTable.remove(group.id());
492 + }
493 +
494 + @Override
495 + public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
496 + // flatten and make iterator unmodifiable
497 + return FluentIterable.from(
498 + getExtraneousGroupIdTable(deviceId).values());
499 + }
365 } 500 }
......
...@@ -40,8 +40,10 @@ import org.onosproject.net.group.GroupBuckets; ...@@ -40,8 +40,10 @@ import org.onosproject.net.group.GroupBuckets;
40 import org.onosproject.net.group.GroupDescription; 40 import org.onosproject.net.group.GroupDescription;
41 import org.onosproject.net.group.GroupEvent; 41 import org.onosproject.net.group.GroupEvent;
42 import org.onosproject.net.group.GroupKey; 42 import org.onosproject.net.group.GroupKey;
43 -import org.onosproject.net.group.GroupStoreDelegate;
44 import org.onosproject.net.group.GroupStore.UpdateType; 43 import org.onosproject.net.group.GroupStore.UpdateType;
44 +import org.onosproject.net.group.GroupStoreDelegate;
45 +
46 +import com.google.common.collect.Iterables;
45 47
46 /** 48 /**
47 * Test of the simple DeviceStore implementation. 49 * Test of the simple DeviceStore implementation.
...@@ -135,8 +137,24 @@ public class SimpleGroupStoreTest { ...@@ -135,8 +137,24 @@ public class SimpleGroupStoreTest {
135 } 137 }
136 } 138 }
137 139
140 + /**
141 + * Tests group store operations. The following operations are tested:
142 + * a)Tests device group audit completion status change
143 + * b)Tests storeGroup operation
144 + * c)Tests getGroupCount operation
145 + * d)Tests getGroup operation
146 + * e)Tests getGroups operation
147 + * f)Tests addOrUpdateGroupEntry operation from southbound
148 + * g)Tests updateGroupDescription for ADD operation from northbound
149 + * h)Tests updateGroupDescription for REMOVE operation from northbound
150 + * i)Tests deleteGroupDescription operation from northbound
151 + * j)Tests removeGroupEntry operation from southbound
152 + */
138 @Test 153 @Test
139 public void testGroupStoreOperations() { 154 public void testGroupStoreOperations() {
155 + // Set the Device AUDIT completed in the store
156 + simpleGroupStore.deviceInitialAuditCompleted(D1);
157 +
140 ApplicationId appId = 158 ApplicationId appId =
141 new DefaultApplicationId(2, "org.groupstore.test"); 159 new DefaultApplicationId(2, "org.groupstore.test");
142 TestGroupKey key = new TestGroupKey("group1"); 160 TestGroupKey key = new TestGroupKey("group1");
...@@ -169,17 +187,17 @@ public class SimpleGroupStoreTest { ...@@ -169,17 +187,17 @@ public class SimpleGroupStoreTest {
169 groupBuckets, 187 groupBuckets,
170 GroupEvent.Type.GROUP_ADD_REQUESTED); 188 GroupEvent.Type.GROUP_ADD_REQUESTED);
171 simpleGroupStore.setDelegate(checkStoreGroupDelegate); 189 simpleGroupStore.setDelegate(checkStoreGroupDelegate);
172 - /* Testing storeGroup operation */ 190 + // Testing storeGroup operation
173 simpleGroupStore.storeGroupDescription(groupDesc); 191 simpleGroupStore.storeGroupDescription(groupDesc);
174 192
175 - /* Testing getGroupCount operation */ 193 + // Testing getGroupCount operation
176 assertEquals(1, simpleGroupStore.getGroupCount(D1)); 194 assertEquals(1, simpleGroupStore.getGroupCount(D1));
177 195
178 - /* Testing getGroup operation */ 196 + // Testing getGroup operation
179 Group createdGroup = simpleGroupStore.getGroup(D1, key); 197 Group createdGroup = simpleGroupStore.getGroup(D1, key);
180 checkStoreGroupDelegate.verifyGroupId(createdGroup.id()); 198 checkStoreGroupDelegate.verifyGroupId(createdGroup.id());
181 199
182 - /* Testing getGroups operation */ 200 + // Testing getGroups operation
183 Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1); 201 Iterable<Group> createdGroups = simpleGroupStore.getGroups(D1);
184 int groupCount = 0; 202 int groupCount = 0;
185 for (Group group:createdGroups) { 203 for (Group group:createdGroups) {
...@@ -189,7 +207,7 @@ public class SimpleGroupStoreTest { ...@@ -189,7 +207,7 @@ public class SimpleGroupStoreTest {
189 assertEquals(1, groupCount); 207 assertEquals(1, groupCount);
190 simpleGroupStore.unsetDelegate(checkStoreGroupDelegate); 208 simpleGroupStore.unsetDelegate(checkStoreGroupDelegate);
191 209
192 - /* Testing addOrUpdateGroupEntry operation from southbound */ 210 + // Testing addOrUpdateGroupEntry operation from southbound
193 InternalGroupStoreDelegate addGroupEntryDelegate = 211 InternalGroupStoreDelegate addGroupEntryDelegate =
194 new InternalGroupStoreDelegate(key, 212 new InternalGroupStoreDelegate(key,
195 groupBuckets, 213 groupBuckets,
...@@ -198,7 +216,7 @@ public class SimpleGroupStoreTest { ...@@ -198,7 +216,7 @@ public class SimpleGroupStoreTest {
198 simpleGroupStore.addOrUpdateGroupEntry(createdGroup); 216 simpleGroupStore.addOrUpdateGroupEntry(createdGroup);
199 simpleGroupStore.unsetDelegate(addGroupEntryDelegate); 217 simpleGroupStore.unsetDelegate(addGroupEntryDelegate);
200 218
201 - /* Testing updateGroupDescription for ADD operation from northbound */ 219 + // Testing updateGroupDescription for ADD operation from northbound
202 TestGroupKey addKey = new TestGroupKey("group1AddBuckets"); 220 TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
203 PortNumber[] newNeighborPorts = {PortNumber.portNumber(41), 221 PortNumber[] newNeighborPorts = {PortNumber.portNumber(41),
204 PortNumber.portNumber(42)}; 222 PortNumber.portNumber(42)};
...@@ -225,19 +243,14 @@ public class SimpleGroupStoreTest { ...@@ -225,19 +243,14 @@ public class SimpleGroupStoreTest {
225 updatedGroupBuckets, 243 updatedGroupBuckets,
226 GroupEvent.Type.GROUP_UPDATE_REQUESTED); 244 GroupEvent.Type.GROUP_UPDATE_REQUESTED);
227 simpleGroupStore.setDelegate(updateGroupDescDelegate); 245 simpleGroupStore.setDelegate(updateGroupDescDelegate);
228 - GroupDescription newGroupDesc = new DefaultGroupDescription(
229 - D1,
230 - Group.Type.SELECT,
231 - toAddGroupBuckets,
232 - addKey,
233 - appId);
234 simpleGroupStore.updateGroupDescription(D1, 246 simpleGroupStore.updateGroupDescription(D1,
235 key, 247 key,
236 UpdateType.ADD, 248 UpdateType.ADD,
237 - newGroupDesc); 249 + toAddGroupBuckets,
250 + addKey);
238 simpleGroupStore.unsetDelegate(updateGroupDescDelegate); 251 simpleGroupStore.unsetDelegate(updateGroupDescDelegate);
239 252
240 - /* Testing updateGroupDescription for REMOVE operation from northbound */ 253 + // Testing updateGroupDescription for REMOVE operation from northbound
241 TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets"); 254 TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
242 List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>(); 255 List<GroupBucket> toRemoveBuckets = new ArrayList<GroupBucket>();
243 toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0)); 256 toRemoveBuckets.add(updatedGroupBuckets.buckets().get(0));
...@@ -252,23 +265,18 @@ public class SimpleGroupStoreTest { ...@@ -252,23 +265,18 @@ public class SimpleGroupStoreTest {
252 remainingGroupBuckets, 265 remainingGroupBuckets,
253 GroupEvent.Type.GROUP_UPDATE_REQUESTED); 266 GroupEvent.Type.GROUP_UPDATE_REQUESTED);
254 simpleGroupStore.setDelegate(removeGroupDescDelegate); 267 simpleGroupStore.setDelegate(removeGroupDescDelegate);
255 - GroupDescription removeGroupDesc = new DefaultGroupDescription(
256 - D1,
257 - Group.Type.SELECT,
258 - toRemoveGroupBuckets,
259 - removeKey,
260 - appId);
261 simpleGroupStore.updateGroupDescription(D1, 268 simpleGroupStore.updateGroupDescription(D1,
262 addKey, 269 addKey,
263 UpdateType.REMOVE, 270 UpdateType.REMOVE,
264 - removeGroupDesc); 271 + toRemoveGroupBuckets,
272 + removeKey);
265 simpleGroupStore.unsetDelegate(removeGroupDescDelegate); 273 simpleGroupStore.unsetDelegate(removeGroupDescDelegate);
266 274
267 - /* Testing getGroup operation */ 275 + // Testing getGroup operation
268 Group existingGroup = simpleGroupStore.getGroup(D1, removeKey); 276 Group existingGroup = simpleGroupStore.getGroup(D1, removeKey);
269 checkStoreGroupDelegate.verifyGroupId(existingGroup.id()); 277 checkStoreGroupDelegate.verifyGroupId(existingGroup.id());
270 278
271 - /* Testing addOrUpdateGroupEntry operation from southbound */ 279 + // Testing addOrUpdateGroupEntry operation from southbound
272 InternalGroupStoreDelegate updateGroupEntryDelegate = 280 InternalGroupStoreDelegate updateGroupEntryDelegate =
273 new InternalGroupStoreDelegate(removeKey, 281 new InternalGroupStoreDelegate(removeKey,
274 remainingGroupBuckets, 282 remainingGroupBuckets,
...@@ -277,7 +285,7 @@ public class SimpleGroupStoreTest { ...@@ -277,7 +285,7 @@ public class SimpleGroupStoreTest {
277 simpleGroupStore.addOrUpdateGroupEntry(existingGroup); 285 simpleGroupStore.addOrUpdateGroupEntry(existingGroup);
278 simpleGroupStore.unsetDelegate(updateGroupEntryDelegate); 286 simpleGroupStore.unsetDelegate(updateGroupEntryDelegate);
279 287
280 - /* Testing deleteGroupDescription operation from northbound */ 288 + // Testing deleteGroupDescription operation from northbound
281 InternalGroupStoreDelegate deleteGroupDescDelegate = 289 InternalGroupStoreDelegate deleteGroupDescDelegate =
282 new InternalGroupStoreDelegate(removeKey, 290 new InternalGroupStoreDelegate(removeKey,
283 remainingGroupBuckets, 291 remainingGroupBuckets,
...@@ -286,7 +294,7 @@ public class SimpleGroupStoreTest { ...@@ -286,7 +294,7 @@ public class SimpleGroupStoreTest {
286 simpleGroupStore.deleteGroupDescription(D1, removeKey); 294 simpleGroupStore.deleteGroupDescription(D1, removeKey);
287 simpleGroupStore.unsetDelegate(deleteGroupDescDelegate); 295 simpleGroupStore.unsetDelegate(deleteGroupDescDelegate);
288 296
289 - /* Testing removeGroupEntry operation from southbound */ 297 + // Testing removeGroupEntry operation from southbound
290 InternalGroupStoreDelegate removeGroupEntryDelegate = 298 InternalGroupStoreDelegate removeGroupEntryDelegate =
291 new InternalGroupStoreDelegate(removeKey, 299 new InternalGroupStoreDelegate(removeKey,
292 remainingGroupBuckets, 300 remainingGroupBuckets,
...@@ -294,17 +302,10 @@ public class SimpleGroupStoreTest { ...@@ -294,17 +302,10 @@ public class SimpleGroupStoreTest {
294 simpleGroupStore.setDelegate(removeGroupEntryDelegate); 302 simpleGroupStore.setDelegate(removeGroupEntryDelegate);
295 simpleGroupStore.removeGroupEntry(existingGroup); 303 simpleGroupStore.removeGroupEntry(existingGroup);
296 304
297 - /* Testing getGroup operation */ 305 + // Testing getGroup operation
298 existingGroup = simpleGroupStore.getGroup(D1, removeKey); 306 existingGroup = simpleGroupStore.getGroup(D1, removeKey);
299 assertEquals(null, existingGroup); 307 assertEquals(null, existingGroup);
300 - Iterable<Group> existingGroups = simpleGroupStore.getGroups(D1); 308 + assertEquals(0, Iterables.size(simpleGroupStore.getGroups(D1)));
301 - groupCount = 0;
302 - for (Group tmp:existingGroups) {
303 - /* To avoid warning */
304 - assertEquals(null, tmp);
305 - groupCount++;
306 - }
307 - assertEquals(0, groupCount);
308 assertEquals(0, simpleGroupStore.getGroupCount(D1)); 309 assertEquals(0, simpleGroupStore.getGroupCount(D1));
309 310
310 simpleGroupStore.unsetDelegate(removeGroupEntryDelegate); 311 simpleGroupStore.unsetDelegate(removeGroupEntryDelegate);
......