blob: 0fcb1fcbe23a4ee153d0aa9d65290a2cecfc5510 [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
Simon Huntff663742015-05-14 13:33:05 -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;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080026import org.onosproject.event.EventDeliveryService;
Simon Huntff663742015-05-14 13:33:05 -070027import org.onosproject.event.ListenerRegistry;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080028import 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
Simon Huntff663742015-05-14 13:33:05 -070051import java.util.Collection;
52import java.util.Collections;
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
Simon Huntff663742015-05-14 13:33:05 -070069 private final ListenerRegistry<GroupEvent, GroupListener>
70 listenerRegistry = new ListenerRegistry<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071 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) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700106 log.trace("In addGroup API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800107 store.storeGroupDescription(groupDesc);
108 }
109
110 /**
111 * Return a group object associated to an application cookie.
112 *
113 * NOTE1: The presence of group object in the system does not
114 * guarantee that the "group" is actually created in device.
115 * GROUP_ADDED notification would confirm the creation of
116 * this group in data plane.
117 *
118 * @param deviceId device identifier
119 * @param appCookie application cookie to be used for lookup
120 * @return group associated with the application cookie or
121 * NULL if Group is not found for the provided cookie
122 */
123 @Override
124 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700125 log.trace("In getGroup API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800126 return store.getGroup(deviceId, appCookie);
127 }
128
129 /**
130 * Append buckets to existing group. The caller can optionally
131 * associate a new cookie during this updation. GROUP_UPDATED or
132 * GROUP_UPDATE_FAILED notifications would be provided along with
133 * cookie depending on the result of the operation on the device.
134 *
135 * @param deviceId device identifier
136 * @param oldCookie cookie to be used to retrieve the existing group
137 * @param buckets immutable list of group bucket to be added
138 * @param newCookie immutable cookie to be used post update operation
139 * @param appId Application Id
140 */
141 @Override
142 public void addBucketsToGroup(DeviceId deviceId,
143 GroupKey oldCookie,
144 GroupBuckets buckets,
145 GroupKey newCookie,
146 ApplicationId appId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700147 log.trace("In addBucketsToGroup API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800148 store.updateGroupDescription(deviceId,
149 oldCookie,
150 UpdateType.ADD,
151 buckets,
152 newCookie);
153 }
154
155 /**
156 * Remove buckets from existing group. The caller can optionally
157 * associate a new cookie during this updation. GROUP_UPDATED or
158 * GROUP_UPDATE_FAILED notifications would be provided along with
159 * cookie depending on the result of the operation on the device.
160 *
161 * @param deviceId device identifier
162 * @param oldCookie cookie to be used to retrieve the existing group
163 * @param buckets immutable list of group bucket to be removed
164 * @param newCookie immutable cookie to be used post update operation
165 * @param appId Application Id
166 */
167 @Override
168 public void removeBucketsFromGroup(DeviceId deviceId,
169 GroupKey oldCookie,
170 GroupBuckets buckets,
171 GroupKey newCookie,
172 ApplicationId appId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700173 log.trace("In removeBucketsFromGroup API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800174 store.updateGroupDescription(deviceId,
175 oldCookie,
176 UpdateType.REMOVE,
177 buckets,
178 newCookie);
179 }
180
181 /**
182 * Delete a group associated to an application cookie.
183 * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
184 * provided along with cookie depending on the result of the
185 * operation on the device.
186 *
187 * @param deviceId device identifier
188 * @param appCookie application cookie to be used for lookup
189 * @param appId Application Id
190 */
191 @Override
192 public void removeGroup(DeviceId deviceId,
193 GroupKey appCookie,
194 ApplicationId appId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700195 log.trace("In removeGroup API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800196 store.deleteGroupDescription(deviceId, appCookie);
197 }
198
199 /**
200 * Retrieve all groups created by an application in the specified device
201 * as seen by current controller instance.
202 *
203 * @param deviceId device identifier
204 * @param appId application id
205 * @return collection of immutable group objects created by the application
206 */
207 @Override
208 public Iterable<Group> getGroups(DeviceId deviceId,
209 ApplicationId appId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700210 log.trace("In getGroups API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800211 return store.getGroups(deviceId);
212 }
213
Jonathan Hart32600692015-03-09 10:38:40 -0700214 @Override
215 public Iterable<Group> getGroups(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700216 log.trace("In getGroups API");
Jonathan Hart32600692015-03-09 10:38:40 -0700217 return store.getGroups(deviceId);
218 }
219
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800220 /**
221 * Adds the specified group listener.
222 *
223 * @param listener group listener
224 */
225 @Override
226 public void addListener(GroupListener listener) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700227 log.trace("In addListener API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800228 listenerRegistry.addListener(listener);
229 }
230
231 /**
232 * Removes the specified group listener.
233 *
234 * @param listener group listener
235 */
236 @Override
237 public void removeListener(GroupListener listener) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700238 log.trace("In removeListener API");
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800239 listenerRegistry.removeListener(listener);
240 }
241
242 @Override
243 protected GroupProviderService createProviderService(GroupProvider provider) {
244 return new InternalGroupProviderService(provider);
245 }
246
247 private class InternalGroupStoreDelegate implements GroupStoreDelegate {
248 @Override
249 public void notify(GroupEvent event) {
250 final Group group = event.subject();
251 GroupProvider groupProvider =
252 getProvider(group.deviceId());
253 GroupOperations groupOps = null;
254 switch (event.type()) {
255 case GROUP_ADD_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800256 log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
257 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800258 GroupOperation groupAddOp = GroupOperation.
259 createAddGroupOperation(group.id(),
260 group.type(),
261 group.buckets());
262 groupOps = new GroupOperations(
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700263 Collections.singletonList(groupAddOp));
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800264 groupProvider.performGroupOperation(group.deviceId(), groupOps);
265 break;
266
267 case GROUP_UPDATE_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800268 log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
269 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800270 GroupOperation groupModifyOp = GroupOperation.
271 createModifyGroupOperation(group.id(),
272 group.type(),
273 group.buckets());
274 groupOps = new GroupOperations(
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700275 Collections.singletonList(groupModifyOp));
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800276 groupProvider.performGroupOperation(group.deviceId(), groupOps);
277 break;
278
279 case GROUP_REMOVE_REQUESTED:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800280 log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
281 group.id(), group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800282 GroupOperation groupDeleteOp = GroupOperation.
283 createDeleteGroupOperation(group.id(),
284 group.type());
285 groupOps = new GroupOperations(
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700286 Collections.singletonList(groupDeleteOp));
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800287 groupProvider.performGroupOperation(group.deviceId(), groupOps);
288 break;
289
290 case GROUP_ADDED:
291 case GROUP_UPDATED:
292 case GROUP_REMOVED:
sangho7ff01812015-02-09 16:21:53 -0800293 case GROUP_ADD_FAILED:
294 case GROUP_UPDATE_FAILED:
295 case GROUP_REMOVE_FAILED:
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800296 eventDispatcher.post(event);
297 break;
298
299 default:
300 break;
301 }
302 }
303 }
304
305 private class InternalGroupProviderService
306 extends AbstractProviderService<GroupProvider>
307 implements GroupProviderService {
308
309 protected InternalGroupProviderService(GroupProvider provider) {
310 super(provider);
311 }
312
313 @Override
sangho7ff01812015-02-09 16:21:53 -0800314 public void groupOperationFailed(DeviceId deviceId,
315 GroupOperation operation) {
316 store.groupOperationFailed(deviceId, operation);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800317 }
318
319 private void groupMissing(Group group) {
320 checkValidity();
321 GroupProvider gp = getProvider(group.deviceId());
322 switch (group.state()) {
323 case PENDING_DELETE:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800324 log.debug("Group {} delete confirmation from device {}",
325 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800326 store.removeGroupEntry(group);
327 break;
328 case ADDED:
329 case PENDING_ADD:
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800330 log.debug("Group {} is in store but not on device {}",
331 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800332 GroupOperation groupAddOp = GroupOperation.
333 createAddGroupOperation(group.id(),
334 group.type(),
335 group.buckets());
336 GroupOperations groupOps = new GroupOperations(
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700337 Collections.singletonList(groupAddOp));
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800338 gp.performGroupOperation(group.deviceId(), groupOps);
339 break;
340 default:
341 log.debug("Group {} has not been installed.", group);
342 break;
343 }
344 }
345
346
347 private void extraneousGroup(Group group) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800348 log.debug("Group {} is on device {} but not in store.",
349 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800350 checkValidity();
351 store.addOrUpdateExtraneousGroupEntry(group);
352 }
353
354 private void groupAdded(Group group) {
355 checkValidity();
356
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800357 log.trace("Group {} Added or Updated in device {}",
358 group, group.deviceId());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800359 store.addOrUpdateGroupEntry(group);
360 }
361
362 @Override
363 public void pushGroupMetrics(DeviceId deviceId,
364 Collection<Group> groupEntries) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800365 log.trace("Received group metrics from device {}",
366 deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800367 boolean deviceInitialAuditStatus =
368 store.deviceInitialAuditStatus(deviceId);
369 Set<Group> southboundGroupEntries =
370 Sets.newHashSet(groupEntries);
371 Set<Group> storedGroupEntries =
372 Sets.newHashSet(store.getGroups(deviceId));
373 Set<Group> extraneousStoredEntries =
374 Sets.newHashSet(store.getExtraneousGroups(deviceId));
375
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700376 log.trace("Displaying all ({}) southboundGroupEntries for device {}",
377 southboundGroupEntries.size(),
378 deviceId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800379 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
380 Group group = it.next();
381 log.trace("Group {} in device {}", group, deviceId);
382 }
383
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700384 log.trace("Displaying all ({}) stored group entries for device {}",
385 storedGroupEntries.size(),
386 deviceId);
387 for (Iterator<Group> it1 = storedGroupEntries.iterator(); it1.hasNext();) {
388 Group group = it1.next();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800389 log.trace("Stored Group {} for device {}", group, deviceId);
390 }
391
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700392 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
393 Group group = it2.next();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800394 if (storedGroupEntries.remove(group)) {
395 // we both have the group, let's update some info then.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800396 log.trace("Group AUDIT: group {} exists "
397 + "in both planes for device {}",
398 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800399 groupAdded(group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700400 it2.remove();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800401 }
402 }
403 for (Group group : southboundGroupEntries) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700404 if (store.getGroup(group.deviceId(), group.id()) != null) {
405 // There is a group existing with the same id
406 // It is possible that group update is
407 // in progress while we got a stale info from switch
408 if (!storedGroupEntries.remove(store.getGroup(
409 group.deviceId(), group.id()))) {
410 log.warn("Group AUDIT: Inconsistent state:"
411 + "Group exists in ID based table while "
412 + "not present in key based table");
413 }
414 } else {
415 // there are groups in the switch that aren't in the store
416 log.trace("Group AUDIT: extraneous group {} exists "
417 + "in data plane for device {}",
418 group.id(), deviceId);
419 extraneousStoredEntries.remove(group);
420 extraneousGroup(group);
421 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800422 }
423 for (Group group : storedGroupEntries) {
424 // there are groups in the store that aren't in the switch
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800425 log.trace("Group AUDIT: group {} missing "
426 + "in data plane for device {}",
427 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800428 groupMissing(group);
429 }
430 for (Group group : extraneousStoredEntries) {
431 // there are groups in the extraneous store that
432 // aren't in the switch
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800433 log.trace("Group AUDIT: clearing extransoeus group {} "
434 + "from store for device {}",
435 group.id(), deviceId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800436 store.removeExtraneousGroupEntry(group);
437 }
438
439 if (!deviceInitialAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800440 log.debug("Group AUDIT: Setting device {} initial "
441 + "AUDIT completed", deviceId);
442 store.deviceInitialAuditCompleted(deviceId, true);
443 }
444 }
445 }
446
447 private class InternalDeviceListener implements DeviceListener {
448
449 @Override
450 public void event(DeviceEvent event) {
451 switch (event.type()) {
452 case DEVICE_REMOVED:
453 log.debug("Clearing device {} initial "
454 + "AUDIT completed status as device is going down",
455 event.subject().id());
456 store.deviceInitialAuditCompleted(event.subject().id(), false);
457 break;
458
459 default:
460 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800461 }
462 }
463 }
464}