blob: 0564fae7d5a112b850c8907eaeffb67395ee95fd [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08003 *
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 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080017
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.FluentIterable;
19import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080020import org.onosproject.core.GroupId;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.group.DefaultGroup;
23import org.onosproject.net.group.DefaultGroupDescription;
24import org.onosproject.net.group.Group;
25import org.onosproject.net.group.Group.GroupState;
26import org.onosproject.net.group.GroupBucket;
27import org.onosproject.net.group.GroupBuckets;
28import org.onosproject.net.group.GroupDescription;
29import org.onosproject.net.group.GroupEvent;
30import org.onosproject.net.group.GroupEvent.Type;
31import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080032import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080033import org.onosproject.net.group.GroupStore;
34import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070035import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080036import org.onosproject.net.group.StoredGroupEntry;
37import org.onosproject.store.AbstractStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070038import org.osgi.service.component.annotations.Activate;
39import org.osgi.service.component.annotations.Component;
40import org.osgi.service.component.annotations.Deactivate;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080041import org.slf4j.Logger;
42
Ray Milkeyd84f89b2018-08-17 14:54:17 -070043import java.util.ArrayList;
44import java.util.Collection;
45import java.util.HashMap;
46import java.util.Iterator;
47import java.util.List;
48import java.util.Map;
49import java.util.Optional;
50import java.util.Set;
51import java.util.concurrent.ConcurrentHashMap;
52import java.util.concurrent.ConcurrentMap;
53import java.util.concurrent.atomic.AtomicInteger;
54
55import static org.slf4j.LoggerFactory.getLogger;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080056
57/**
58 * Manages inventory of group entries using trivial in-memory implementation.
59 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070060@Component(immediate = true, service = GroupStore.class)
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080061public class SimpleGroupStore
62 extends AbstractStore<GroupEvent, GroupStoreDelegate>
63 implements GroupStore {
64
65 private final Logger log = getLogger(getClass());
66
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080067 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -080068 private final GroupId dummyGroupId = new GroupId(dummyId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080069
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080070 // inner Map is per device group table
71 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
72 groupEntriesByKey = new ConcurrentHashMap<>();
73 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
74 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080075 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
76 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
77 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
78 extraneousGroupEntriesById = new ConcurrentHashMap<>();
79
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070080 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080081
82 private final AtomicInteger groupIdGen = new AtomicInteger();
83
84 @Activate
85 public void activate() {
86 log.info("Started");
87 }
88
89 @Deactivate
90 public void deactivate() {
91 groupEntriesByKey.clear();
92 groupEntriesById.clear();
93 log.info("Stopped");
94 }
95
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080096 /**
97 * Returns the group key table for specified device.
98 *
99 * @param deviceId identifier of the device
100 * @return Map representing group key table of given device.
101 */
102 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700103 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800104 }
105
106 /**
107 * Returns the group id table for specified device.
108 *
109 * @param deviceId identifier of the device
110 * @return Map representing group key table of given device.
111 */
112 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700113 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800114 }
115
116 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800117 * Returns the pending group key table for specified device.
118 *
119 * @param deviceId identifier of the device
120 * @return Map representing group key table of given device.
121 */
122 private ConcurrentMap<GroupKey, StoredGroupEntry>
123 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700124 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800125 }
126
127 /**
128 * Returns the extraneous group id table for specified device.
129 *
130 * @param deviceId identifier of the device
131 * @return Map representing group key table of given device.
132 */
133 private ConcurrentMap<GroupId, Group>
134 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700135 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800136 }
137
138 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800139 * Returns the number of groups for the specified device in the store.
140 *
141 * @return number of groups for the specified device
142 */
143 @Override
144 public int getGroupCount(DeviceId deviceId) {
145 return (groupEntriesByKey.get(deviceId) != null) ?
146 groupEntriesByKey.get(deviceId).size() : 0;
147 }
148
149 /**
150 * Returns the groups associated with a device.
151 *
152 * @param deviceId the device ID
153 *
154 * @return the group entries
155 */
156 @Override
157 public Iterable<Group> getGroups(DeviceId deviceId) {
158 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800159 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700160 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800161 }
162
163 /**
164 * Returns the stored group entry.
165 *
166 * @param deviceId the device ID
167 * @param appCookie the group key
168 *
169 * @return a group associated with the key
170 */
171 @Override
172 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
173 return (groupEntriesByKey.get(deviceId) != null) ?
174 groupEntriesByKey.get(deviceId).get(appCookie) :
175 null;
176 }
177
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700178 @Override
179 public Group getGroup(DeviceId deviceId, GroupId groupId) {
180 return (groupEntriesById.get(deviceId) != null) ?
181 groupEntriesById.get(deviceId).get(groupId) :
182 null;
183 }
184
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800185 private int getFreeGroupIdValue(DeviceId deviceId) {
186 int freeId = groupIdGen.incrementAndGet();
187
188 while (true) {
189 Group existing = (
190 groupEntriesById.get(deviceId) != null) ?
Yi Tsengfa394de2017-02-01 11:26:40 -0800191 groupEntriesById.get(deviceId).get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800192 null;
193 if (existing == null) {
194 existing = (
195 extraneousGroupEntriesById.get(deviceId) != null) ?
196 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800197 get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800198 null;
199 }
200 if (existing != null) {
201 freeId = groupIdGen.incrementAndGet();
202 } else {
203 break;
204 }
205 }
206 return freeId;
207 }
208
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800209 /**
210 * Stores a new group entry using the information from group description.
211 *
212 * @param groupDesc group description to be used to create group entry
213 */
214 @Override
215 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800216 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800217 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
218 return;
219 }
220
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800221 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
222 // Device group audit has not completed yet
223 // Add this group description to pending group key table
224 // Create a group entry object with Dummy Group ID
225 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
226 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
227 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
228 getPendingGroupKeyTable(groupDesc.deviceId());
229 pendingKeyTable.put(groupDesc.appCookie(), group);
230 return;
231 }
232
233 storeGroupDescriptionInternal(groupDesc);
234 }
235
236 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800237 // Check if a group is existing with the same key
238 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
239 return;
240 }
241
Saurav Das100e3b82015-04-30 11:12:10 -0700242 GroupId id = null;
243 if (groupDesc.givenGroupId() == null) {
244 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800245 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700246 } else {
Yi Tsengfa394de2017-02-01 11:26:40 -0800247 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700248 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800249 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800250 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800251 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800252 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
253 getGroupKeyTable(groupDesc.deviceId());
254 keyTable.put(groupDesc.appCookie(), group);
255 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
256 getGroupIdTable(groupDesc.deviceId());
257 idTable.put(id, group);
258 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
259 group));
260 }
261
262 /**
263 * Updates the existing group entry with the information
264 * from group description.
265 *
266 * @param deviceId the device ID
267 * @param oldAppCookie the current group key
268 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800269 * @param newBuckets group buckets for updates
270 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800271 */
272 @Override
273 public void updateGroupDescription(DeviceId deviceId,
274 GroupKey oldAppCookie,
275 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800276 GroupBuckets newBuckets,
277 GroupKey newAppCookie) {
278 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800279 Group oldGroup = getGroup(deviceId, oldAppCookie);
280 if (oldGroup == null) {
281 return;
282 }
283
284 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
285 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800286 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800287 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800288 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800289 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
292 oldGroup.deviceId(),
293 oldGroup.type(),
294 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800295 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700296 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800297 oldGroup.appId());
298 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
299 updatedGroupDesc);
300 newGroup.setState(GroupState.PENDING_UPDATE);
301 newGroup.setLife(oldGroup.life());
302 newGroup.setPackets(oldGroup.packets());
303 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800304 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800305 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
306 getGroupKeyTable(oldGroup.deviceId());
307 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
308 getGroupIdTable(oldGroup.deviceId());
309 keyTable.remove(oldGroup.appCookie());
310 idTable.remove(oldGroup.id());
311 keyTable.put(newGroup.appCookie(), newGroup);
312 idTable.put(newGroup.id(), newGroup);
313 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
314 }
315 }
316
317 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
Victor Silvadf1eeae2016-08-12 15:28:57 -0300318 UpdateType type,
319 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300320 if (type == UpdateType.SET) {
321 return buckets.buckets();
322 }
323
Victor Silvadf1eeae2016-08-12 15:28:57 -0300324 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
325 List<GroupBucket> updatedBucketList = new ArrayList<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800326 boolean groupDescUpdated = false;
327
328 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300329 List<GroupBucket> newBuckets = buckets.buckets();
330
331 // Add old buckets that will not be updated and check if any will be updated.
332 for (GroupBucket oldBucket : oldBuckets) {
333 int newBucketIndex = newBuckets.indexOf(oldBucket);
334
335 if (newBucketIndex != -1) {
336 GroupBucket newBucket = newBuckets.get(newBucketIndex);
337 if (!newBucket.hasSameParameters(oldBucket)) {
338 // Bucket will be updated
339 groupDescUpdated = true;
340 }
341 } else {
342 // Old bucket will remain the same - add it.
343 updatedBucketList.add(oldBucket);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800344 }
345 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300346
347 // Add all new buckets
348 updatedBucketList.addAll(newBuckets);
349 if (!oldBuckets.containsAll(newBuckets)) {
350 groupDescUpdated = true;
351 }
352
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800353 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300354 List<GroupBucket> bucketsToRemove = buckets.buckets();
355
356 // Check which old buckets should remain
357 for (GroupBucket oldBucket : oldBuckets) {
358 if (!bucketsToRemove.contains(oldBucket)) {
359 updatedBucketList.add(oldBucket);
360 } else {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800361 groupDescUpdated = true;
362 }
363 }
364 }
365
366 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300367 return updatedBucketList;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800368 } else {
369 return null;
370 }
371 }
372
373 /**
374 * Triggers deleting the existing group entry.
375 *
376 * @param deviceId the device ID
377 * @param appCookie the group key
378 */
379 @Override
380 public void deleteGroupDescription(DeviceId deviceId,
381 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800382 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800383 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
384 groupEntriesByKey.get(deviceId).get(appCookie) :
385 null;
386 if (existing == null) {
387 return;
388 }
389
390 synchronized (existing) {
391 existing.setState(GroupState.PENDING_DELETE);
392 }
393 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
394 }
395
396 /**
397 * Stores a new group entry, or updates an existing entry.
398 *
399 * @param group group entry
400 */
401 @Override
402 public void addOrUpdateGroupEntry(Group group) {
403 // check if this new entry is an update to an existing entry
404 StoredGroupEntry existing = (groupEntriesById.get(
405 group.deviceId()) != null) ?
406 groupEntriesById.get(group.deviceId()).get(group.id()) :
407 null;
408 GroupEvent event = null;
409
410 if (existing != null) {
411 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700412 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700413 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700414 existing.buckets().buckets()
415 .stream()
Ray Milkey88cc3432017-03-30 17:19:08 -0700416 .filter((existingBucket) -> (existingBucket.equals(bucket)))
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700417 .findFirst();
418 if (matchingBucket.isPresent()) {
419 ((StoredGroupBucketEntry) matchingBucket.
420 get()).setPackets(bucket.packets());
421 ((StoredGroupBucketEntry) matchingBucket.
422 get()).setBytes(bucket.bytes());
423 } else {
424 log.warn("addOrUpdateGroupEntry: No matching "
425 + "buckets to update stats");
426 }
427 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800428 existing.setLife(group.life());
429 existing.setPackets(group.packets());
430 existing.setBytes(group.bytes());
431 if (existing.state() == GroupState.PENDING_ADD) {
432 existing.setState(GroupState.ADDED);
433 event = new GroupEvent(Type.GROUP_ADDED, existing);
434 } else {
435 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700436 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800437 }
438 event = new GroupEvent(Type.GROUP_UPDATED, existing);
439 }
440 }
441 }
442
443 if (event != null) {
444 notifyDelegate(event);
445 }
446 }
447
448 /**
449 * Removes the group entry from store.
450 *
451 * @param group group entry
452 */
453 @Override
454 public void removeGroupEntry(Group group) {
455 StoredGroupEntry existing = (groupEntriesById.get(
456 group.deviceId()) != null) ?
457 groupEntriesById.get(group.deviceId()).get(group.id()) :
458 null;
459
460 if (existing != null) {
461 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
462 getGroupKeyTable(existing.deviceId());
463 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
464 getGroupIdTable(existing.deviceId());
465 idTable.remove(existing.id());
466 keyTable.remove(existing.appCookie());
467 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
468 }
469 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800470
471 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800472 public void purgeGroupEntry(DeviceId deviceId) {
473 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
474 groupEntriesById.get(deviceId).entrySet();
475
476 groupEntriesById.remove(deviceId);
477 groupEntriesByKey.remove(deviceId);
478
479 entryPendingRemove.forEach(entry -> {
480 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
481 });
482 }
483
484 @Override
Victor Silva4e8b7832016-08-17 17:11:19 -0300485 public void purgeGroupEntries() {
486 groupEntriesById.values().forEach(groupEntries -> {
487 groupEntries.entrySet().forEach(entry -> {
488 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
489 });
490 });
491
492 groupEntriesById.clear();
493 groupEntriesByKey.clear();
494 }
495
496 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800497 public void deviceInitialAuditCompleted(DeviceId deviceId,
498 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800499 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800500 if (completed) {
501 log.debug("deviceInitialAuditCompleted: AUDIT "
502 + "completed for device {}", deviceId);
503 deviceAuditStatus.put(deviceId, true);
504 // Execute all pending group requests
505 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
506 getPendingGroupKeyTable(deviceId);
507 for (Group group:pendingGroupRequests.values()) {
508 GroupDescription tmp = new DefaultGroupDescription(
509 group.deviceId(),
510 group.type(),
511 group.buckets(),
512 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700513 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800514 group.appId());
515 storeGroupDescriptionInternal(tmp);
516 }
517 getPendingGroupKeyTable(deviceId).clear();
518 } else {
519 if (deviceAuditStatus.get(deviceId)) {
520 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
521 + "status for device {}", deviceId);
522 deviceAuditStatus.put(deviceId, false);
523 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800524 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800525 }
526 }
527
528 @Override
529 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
530 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800531 return (deviceAuditStatus.get(deviceId) != null)
532 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800533 }
534 }
535
536 @Override
sangho7ff01812015-02-09 16:21:53 -0800537 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
538
539 StoredGroupEntry existing = (groupEntriesById.get(
540 deviceId) != null) ?
541 groupEntriesById.get(deviceId).get(operation.groupId()) :
542 null;
543
544 if (existing == null) {
545 log.warn("No group entry with ID {} found ", operation.groupId());
546 return;
547 }
548
549 switch (operation.opType()) {
550 case ADD:
551 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
552 break;
553 case MODIFY:
554 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
555 break;
556 case DELETE:
557 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
558 break;
559 default:
560 log.warn("Unknown group operation type {}", operation.opType());
561 }
562
563 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
564 getGroupKeyTable(existing.deviceId());
565 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
566 getGroupIdTable(existing.deviceId());
567 idTable.remove(existing.id());
568 keyTable.remove(existing.appCookie());
569 }
570
571 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800572 public void addOrUpdateExtraneousGroupEntry(Group group) {
573 ConcurrentMap<GroupId, Group> extraneousIdTable =
574 getExtraneousGroupIdTable(group.deviceId());
575 extraneousIdTable.put(group.id(), group);
576 // Check the reference counter
577 if (group.referenceCount() == 0) {
578 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
579 }
580 }
581
582 @Override
583 public void removeExtraneousGroupEntry(Group group) {
584 ConcurrentMap<GroupId, Group> extraneousIdTable =
585 getExtraneousGroupIdTable(group.deviceId());
586 extraneousIdTable.remove(group.id());
587 }
588
589 @Override
590 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
591 // flatten and make iterator unmodifiable
592 return FluentIterable.from(
593 getExtraneousGroupIdTable(deviceId).values());
594 }
sangho7ff01812015-02-09 16:21:53 -0800595
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700596 @Override
597 public void pushGroupMetrics(DeviceId deviceId,
598 Collection<Group> groupEntries) {
599 boolean deviceInitialAuditStatus =
600 deviceInitialAuditStatus(deviceId);
601 Set<Group> southboundGroupEntries =
602 Sets.newHashSet(groupEntries);
603 Set<Group> storedGroupEntries =
604 Sets.newHashSet(getGroups(deviceId));
605 Set<Group> extraneousStoredEntries =
606 Sets.newHashSet(getExtraneousGroups(deviceId));
607
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700608 if (log.isTraceEnabled()) {
609 log.trace("pushGroupMetrics: Displaying all ({}) "
610 + "southboundGroupEntries for device {}",
611 southboundGroupEntries.size(),
612 deviceId);
613 for (Group group : southboundGroupEntries) {
614 log.trace("Group {} in device {}", group, deviceId);
615 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700616
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700617 log.trace("Displaying all ({}) stored group entries for device {}",
618 storedGroupEntries.size(),
619 deviceId);
620 for (Group group : storedGroupEntries) {
621 log.trace("Stored Group {} for device {}", group, deviceId);
622 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700623 }
624
625 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
626 Group group = it2.next();
627 if (storedGroupEntries.remove(group)) {
628 // we both have the group, let's update some info then.
629 log.trace("Group AUDIT: group {} exists "
630 + "in both planes for device {}",
631 group.id(), deviceId);
632 groupAdded(group);
633 it2.remove();
634 }
635 }
636 for (Group group : southboundGroupEntries) {
637 if (getGroup(group.deviceId(), group.id()) != null) {
638 // There is a group existing with the same id
639 // It is possible that group update is
640 // in progress while we got a stale info from switch
641 if (!storedGroupEntries.remove(getGroup(
642 group.deviceId(), group.id()))) {
643 log.warn("Group AUDIT: Inconsistent state:"
644 + "Group exists in ID based table while "
645 + "not present in key based table");
646 }
647 } else {
648 // there are groups in the switch that aren't in the store
649 log.trace("Group AUDIT: extraneous group {} exists "
650 + "in data plane for device {}",
651 group.id(), deviceId);
652 extraneousStoredEntries.remove(group);
653 extraneousGroup(group);
654 }
655 }
656 for (Group group : storedGroupEntries) {
657 // there are groups in the store that aren't in the switch
658 log.trace("Group AUDIT: group {} missing "
659 + "in data plane for device {}",
660 group.id(), deviceId);
661 groupMissing(group);
662 }
663 for (Group group : extraneousStoredEntries) {
664 // there are groups in the extraneous store that
665 // aren't in the switch
666 log.trace("Group AUDIT: clearing extransoeus group {} "
667 + "from store for device {}",
668 group.id(), deviceId);
669 removeExtraneousGroupEntry(group);
670 }
671
672 if (!deviceInitialAuditStatus) {
673 log.debug("Group AUDIT: Setting device {} initial "
674 + "AUDIT completed", deviceId);
675 deviceInitialAuditCompleted(deviceId, true);
676 }
677 }
678
helenyrwu89470f12016-08-12 13:18:10 -0700679 @Override
680 public void notifyOfFailovers(Collection<Group> failoverGroups) {
681 List<GroupEvent> failoverEvents = new ArrayList<>();
682 failoverGroups.forEach(group -> {
683 if (group.type() == Group.Type.FAILOVER) {
684 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
685 }
686 });
687 notifyDelegate(failoverEvents);
688 }
689
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700690 private void groupMissing(Group group) {
691 switch (group.state()) {
692 case PENDING_DELETE:
693 log.debug("Group {} delete confirmation from device {}",
694 group, group.deviceId());
695 removeGroupEntry(group);
696 break;
697 case ADDED:
698 case PENDING_ADD:
699 case PENDING_UPDATE:
700 log.debug("Group {} is in store but not on device {}",
701 group, group.deviceId());
702 StoredGroupEntry existing = (groupEntriesById.get(
703 group.deviceId()) != null) ?
704 groupEntriesById.get(group.deviceId()).get(group.id()) :
705 null;
706 log.trace("groupMissing: group "
707 + "entry {} in device {} moving "
708 + "from {} to PENDING_ADD",
709 existing.id(),
710 existing.deviceId(),
711 existing.state());
712 existing.setState(Group.GroupState.PENDING_ADD);
713 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
714 group));
715 break;
716 default:
717 log.debug("Group {} has not been installed.", group);
718 break;
719 }
720 }
721
722 private void extraneousGroup(Group group) {
723 log.debug("Group {} is on device {} but not in store.",
724 group, group.deviceId());
725 addOrUpdateExtraneousGroupEntry(group);
726 }
727
728 private void groupAdded(Group group) {
729 log.trace("Group {} Added or Updated in device {}",
730 group, group.deviceId());
731 addOrUpdateGroupEntry(group);
732 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800733}