blob: 2d8f81c2889526255430ccbb7a61683f9dcf6105 [file] [log] [blame]
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -08001/*
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 */
16package org.onosproject.net.group.impl;
17
Jonathan Hart32600692015-03-09 10:38:40 -070018import com.google.common.collect.Sets;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.event.AbstractListenerRegistry;
27import org.onosproject.event.EventDeliveryService;
28import org.onosproject.net.DeviceId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080029import org.onosproject.net.device.DeviceEvent;
30import org.onosproject.net.device.DeviceListener;
31import org.onosproject.net.device.DeviceService;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080032import org.onosproject.net.group.Group;
33import org.onosproject.net.group.GroupBuckets;
34import org.onosproject.net.group.GroupDescription;
35import org.onosproject.net.group.GroupEvent;
36import org.onosproject.net.group.GroupKey;
37import org.onosproject.net.group.GroupListener;
38import org.onosproject.net.group.GroupOperation;
39import org.onosproject.net.group.GroupOperations;
40import org.onosproject.net.group.GroupProvider;
41import org.onosproject.net.group.GroupProviderRegistry;
42import org.onosproject.net.group.GroupProviderService;
43import org.onosproject.net.group.GroupService;
44import org.onosproject.net.group.GroupStore;
45import org.onosproject.net.group.GroupStore.UpdateType;
46import org.onosproject.net.group.GroupStoreDelegate;
47import org.onosproject.net.provider.AbstractProviderRegistry;
48import org.onosproject.net.provider.AbstractProviderService;
49import org.slf4j.Logger;
50
Jonathan Hart32600692015-03-09 10:38:40 -070051import java.util.Arrays;
52import java.util.Collection;
53import java.util.Iterator;
54import java.util.Set;
55
56import static org.slf4j.LoggerFactory.getLogger;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080057
58/**
59 * Provides implementation of the group service APIs.
60 */
61@Component(immediate = true)
62@Service
63public class GroupManager
64 extends AbstractProviderRegistry<GroupProvider, GroupProviderService>
65 implements GroupService, GroupProviderRegistry {
66
67 private final Logger log = getLogger(getClass());
68
69 private final AbstractListenerRegistry<GroupEvent, GroupListener>
70 listenerRegistry = new AbstractListenerRegistry<>();
71 private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080072 private final DeviceListener deviceListener = new InternalDeviceListener();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080073
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected GroupStore store;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080078 protected DeviceService deviceService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080081 protected EventDeliveryService eventDispatcher;
82
83 @Activate
84 public void activate() {
85 store.setDelegate(delegate);
86 eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080087 deviceService.addListener(deviceListener);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080088 log.info("Started");
89 }
90
91 @Deactivate
92 public void deactivate() {
93 store.unsetDelegate(delegate);
94 eventDispatcher.removeSink(GroupEvent.class);
95 log.info("Stopped");
96 }
97
98 /**
99 * Create a group in the specified device with the provided parameters.
100 *
101 * @param groupDesc group creation parameters
102 *
103 */
104 @Override
105 public void addGroup(GroupDescription groupDesc) {
106 store.storeGroupDescription(groupDesc);
107 }
108
109 /**
110 * Return a group object associated to an application cookie.
111 *
112 * NOTE1: The presence of group object in the system does not
113 * guarantee that the "group" is actually created in device.
114 * GROUP_ADDED notification would confirm the creation of
115 * this group in data plane.
116 *
117 * @param deviceId device identifier
118 * @param appCookie application cookie to be used for lookup
119 * @return group associated with the application cookie or
120 * NULL if Group is not found for the provided cookie
121 */
122 @Override
123 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
124 return store.getGroup(deviceId, appCookie);
125 }
126
127 /**
128 * Append buckets to existing group. The caller can optionally
129 * associate a new cookie during this updation. GROUP_UPDATED or
130 * GROUP_UPDATE_FAILED notifications would be provided along with
131 * cookie depending on the result of the operation on the device.
132 *
133 * @param deviceId device identifier
134 * @param oldCookie cookie to be used to retrieve the existing group
135 * @param buckets immutable list of group bucket to be added
136 * @param newCookie immutable cookie to be used post update operation
137 * @param appId Application Id
138 */
139 @Override
140 public void addBucketsToGroup(DeviceId deviceId,
141 GroupKey oldCookie,
142 GroupBuckets buckets,
143 GroupKey newCookie,
144 ApplicationId appId) {
145 store.updateGroupDescription(deviceId,
146 oldCookie,
147 UpdateType.ADD,
148 buckets,
149 newCookie);
150 }
151
152 /**
153 * Remove buckets from existing group. The caller can optionally
154 * associate a new cookie during this updation. GROUP_UPDATED or
155 * GROUP_UPDATE_FAILED notifications would be provided along with
156 * cookie depending on the result of the operation on the device.
157 *
158 * @param deviceId device identifier
159 * @param oldCookie cookie to be used to retrieve the existing group
160 * @param buckets immutable list of group bucket to be removed
161 * @param newCookie immutable cookie to be used post update operation
162 * @param appId Application Id
163 */
164 @Override
165 public void removeBucketsFromGroup(DeviceId deviceId,
166 GroupKey oldCookie,
167 GroupBuckets buckets,
168 GroupKey newCookie,
169 ApplicationId appId) {
170 store.updateGroupDescription(deviceId,
171 oldCookie,
172 UpdateType.REMOVE,
173 buckets,
174 newCookie);
175 }
176
177 /**
178 * Delete a group associated to an application cookie.
179 * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
180 * provided along with cookie depending on the result of the
181 * operation on the device.
182 *
183 * @param deviceId device identifier
184 * @param appCookie application cookie to be used for lookup
185 * @param appId Application Id
186 */
187 @Override
188 public void removeGroup(DeviceId deviceId,
189 GroupKey appCookie,
190 ApplicationId appId) {
191 store.deleteGroupDescription(deviceId, appCookie);
192 }
193
194 /**
195 * Retrieve all groups created by an application in the specified device
196 * as seen by current controller instance.
197 *
198 * @param deviceId device identifier
199 * @param appId application id
200 * @return collection of immutable group objects created by the application
201 */
202 @Override
203 public Iterable<Group> getGroups(DeviceId deviceId,
204 ApplicationId appId) {
205 return store.getGroups(deviceId);
206 }
207
Jonathan Hart32600692015-03-09 10:38:40 -0700208 @Override
209 public Iterable<Group> getGroups(DeviceId deviceId) {
210 return store.getGroups(deviceId);
211 }
212
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800213 /**
214 * Adds the specified group listener.
215 *
216 * @param listener group listener
217 */
218 @Override
219 public void addListener(GroupListener listener) {
220 listenerRegistry.addListener(listener);
221 }
222
223 /**
224 * Removes the specified group listener.
225 *
226 * @param listener group listener
227 */
228 @Override
229 public void removeListener(GroupListener listener) {
230 listenerRegistry.removeListener(listener);
231 }
232
233 @Override
234 protected GroupProviderService createProviderService(GroupProvider provider) {
235 return new InternalGroupProviderService(provider);
236 }
237
238 private class InternalGroupStoreDelegate implements GroupStoreDelegate {
239 @Override
240 public void notify(GroupEvent event) {
241 final Group group = event.subject();
242 GroupProvider groupProvider =
243 getProvider(group.deviceId());
244 GroupOperations groupOps = null;
245 switch (event.type()) {
246 case GROUP_ADD_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800247 log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
248 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800249 GroupOperation groupAddOp = GroupOperation.
250 createAddGroupOperation(group.id(),
251 group.type(),
252 group.buckets());
253 groupOps = new GroupOperations(
254 Arrays.asList(groupAddOp));
255 groupProvider.performGroupOperation(group.deviceId(), groupOps);
256 break;
257
258 case GROUP_UPDATE_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800259 log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
260 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800261 GroupOperation groupModifyOp = GroupOperation.
262 createModifyGroupOperation(group.id(),
263 group.type(),
264 group.buckets());
265 groupOps = new GroupOperations(
266 Arrays.asList(groupModifyOp));
267 groupProvider.performGroupOperation(group.deviceId(), groupOps);
268 break;
269
270 case GROUP_REMOVE_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800271 log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
272 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800273 GroupOperation groupDeleteOp = GroupOperation.
274 createDeleteGroupOperation(group.id(),
275 group.type());
276 groupOps = new GroupOperations(
277 Arrays.asList(groupDeleteOp));
278 groupProvider.performGroupOperation(group.deviceId(), groupOps);
279 break;
280
281 case GROUP_ADDED:
282 case GROUP_UPDATED:
283 case GROUP_REMOVED:
sangho7ff01812015-02-09 16:21:53 -0800284 case GROUP_ADD_FAILED:
285 case GROUP_UPDATE_FAILED:
286 case GROUP_REMOVE_FAILED:
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800287 eventDispatcher.post(event);
288 break;
289
290 default:
291 break;
292 }
293 }
294 }
295
296 private class InternalGroupProviderService
297 extends AbstractProviderService<GroupProvider>
298 implements GroupProviderService {
299
300 protected InternalGroupProviderService(GroupProvider provider) {
301 super(provider);
302 }
303
304 @Override
sangho7ff01812015-02-09 16:21:53 -0800305 public void groupOperationFailed(DeviceId deviceId,
306 GroupOperation operation) {
307 store.groupOperationFailed(deviceId, operation);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 }
309
310 private void groupMissing(Group group) {
311 checkValidity();
312 GroupProvider gp = getProvider(group.deviceId());
313 switch (group.state()) {
314 case PENDING_DELETE:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800315 log.debug("Group {} delete confirmation from device {}",
316 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800317 store.removeGroupEntry(group);
318 break;
319 case ADDED:
320 case PENDING_ADD:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800321 log.debug("Group {} is in store but not on device {}",
322 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800323 GroupOperation groupAddOp = GroupOperation.
324 createAddGroupOperation(group.id(),
325 group.type(),
326 group.buckets());
327 GroupOperations groupOps = new GroupOperations(
328 Arrays.asList(groupAddOp));
329 gp.performGroupOperation(group.deviceId(), groupOps);
330 break;
331 default:
332 log.debug("Group {} has not been installed.", group);
333 break;
334 }
335 }
336
337
338 private void extraneousGroup(Group group) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800339 log.debug("Group {} is on device {} but not in store.",
340 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800341 checkValidity();
342 store.addOrUpdateExtraneousGroupEntry(group);
343 }
344
345 private void groupAdded(Group group) {
346 checkValidity();
347
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800348 log.trace("Group {} Added or Updated in device {}",
349 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800350 store.addOrUpdateGroupEntry(group);
351 }
352
353 @Override
354 public void pushGroupMetrics(DeviceId deviceId,
355 Collection<Group> groupEntries) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800356 log.trace("Received group metrics from device {}",
357 deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800358 boolean deviceInitialAuditStatus =
359 store.deviceInitialAuditStatus(deviceId);
360 Set<Group> southboundGroupEntries =
361 Sets.newHashSet(groupEntries);
362 Set<Group> storedGroupEntries =
363 Sets.newHashSet(store.getGroups(deviceId));
364 Set<Group> extraneousStoredEntries =
365 Sets.newHashSet(store.getExtraneousGroups(deviceId));
366
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800367 log.trace("Displaying all southboundGroupEntries for device {}", deviceId);
368 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
369 Group group = it.next();
370 log.trace("Group {} in device {}", group, deviceId);
371 }
372
373 log.trace("Displaying all stored group entries for device {}", deviceId);
374 for (Iterator<Group> it = storedGroupEntries.iterator(); it.hasNext();) {
375 Group group = it.next();
376 log.trace("Stored Group {} for device {}", group, deviceId);
377 }
378
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800379 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
380 Group group = it.next();
381 if (storedGroupEntries.remove(group)) {
382 // we both have the group, let's update some info then.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800383 log.trace("Group AUDIT: group {} exists "
384 + "in both planes for device {}",
385 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800386 groupAdded(group);
387 it.remove();
388 }
389 }
390 for (Group group : southboundGroupEntries) {
391 // there are groups in the switch that aren't in the store
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800392 log.trace("Group AUDIT: extraneous group {} exists "
393 + "in data plane for device {}",
394 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800395 extraneousStoredEntries.remove(group);
396 extraneousGroup(group);
397 }
398 for (Group group : storedGroupEntries) {
399 // there are groups in the store that aren't in the switch
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800400 log.trace("Group AUDIT: group {} missing "
401 + "in data plane for device {}",
402 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800403 groupMissing(group);
404 }
405 for (Group group : extraneousStoredEntries) {
406 // there are groups in the extraneous store that
407 // aren't in the switch
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800408 log.trace("Group AUDIT: clearing extransoeus group {} "
409 + "from store for device {}",
410 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800411 store.removeExtraneousGroupEntry(group);
412 }
413
414 if (!deviceInitialAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800415 log.debug("Group AUDIT: Setting device {} initial "
416 + "AUDIT completed", deviceId);
417 store.deviceInitialAuditCompleted(deviceId, true);
418 }
419 }
420 }
421
422 private class InternalDeviceListener implements DeviceListener {
423
424 @Override
425 public void event(DeviceEvent event) {
426 switch (event.type()) {
427 case DEVICE_REMOVED:
428 log.debug("Clearing device {} initial "
429 + "AUDIT completed status as device is going down",
430 event.subject().id());
431 store.deviceInitialAuditCompleted(event.subject().id(), false);
432 break;
433
434 default:
435 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800436 }
437 }
438 }
439}