blob: 01930ef8ee53820f931c3165247f5ce474d1095a [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080018import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import java.util.Collection;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080022import java.util.HashMap;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070023import java.util.Iterator;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080024import java.util.List;
Charles Chan0c7c43b2016-01-14 17:39:20 -080025import java.util.Map;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070026import java.util.Optional;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070027import java.util.Set;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080028import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.ConcurrentMap;
30import java.util.concurrent.atomic.AtomicInteger;
31
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080036import org.onosproject.core.DefaultGroupId;
37import org.onosproject.core.GroupId;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.group.DefaultGroup;
40import org.onosproject.net.group.DefaultGroupDescription;
41import org.onosproject.net.group.Group;
42import org.onosproject.net.group.Group.GroupState;
43import org.onosproject.net.group.GroupBucket;
44import org.onosproject.net.group.GroupBuckets;
45import org.onosproject.net.group.GroupDescription;
46import org.onosproject.net.group.GroupEvent;
47import org.onosproject.net.group.GroupEvent.Type;
48import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080049import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080050import org.onosproject.net.group.GroupStore;
51import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070052import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080053import org.onosproject.net.group.StoredGroupEntry;
54import org.onosproject.store.AbstractStore;
55import org.slf4j.Logger;
56
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080057import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070058import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080059
60/**
61 * Manages inventory of group entries using trivial in-memory implementation.
62 */
63@Component(immediate = true)
64@Service
65public class SimpleGroupStore
66 extends AbstractStore<GroupEvent, GroupStoreDelegate>
67 implements GroupStore {
68
69 private final Logger log = getLogger(getClass());
70
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071 private final int dummyId = 0xffffffff;
72 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
73
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080074 // inner Map is per device group table
75 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
76 groupEntriesByKey = new ConcurrentHashMap<>();
77 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
78 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080079 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
80 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
81 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
82 extraneousGroupEntriesById = new ConcurrentHashMap<>();
83
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070084 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080085
86 private final AtomicInteger groupIdGen = new AtomicInteger();
87
88 @Activate
89 public void activate() {
90 log.info("Started");
91 }
92
93 @Deactivate
94 public void deactivate() {
95 groupEntriesByKey.clear();
96 groupEntriesById.clear();
97 log.info("Stopped");
98 }
99
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800100 /**
101 * Returns the group key table for specified device.
102 *
103 * @param deviceId identifier of the device
104 * @return Map representing group key table of given device.
105 */
106 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700107 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800108 }
109
110 /**
111 * Returns the group id table for specified device.
112 *
113 * @param deviceId identifier of the device
114 * @return Map representing group key table of given device.
115 */
116 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700117 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800118 }
119
120 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800121 * Returns the pending group key table for specified device.
122 *
123 * @param deviceId identifier of the device
124 * @return Map representing group key table of given device.
125 */
126 private ConcurrentMap<GroupKey, StoredGroupEntry>
127 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700128 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800129 }
130
131 /**
132 * Returns the extraneous group id table for specified device.
133 *
134 * @param deviceId identifier of the device
135 * @return Map representing group key table of given device.
136 */
137 private ConcurrentMap<GroupId, Group>
138 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700139 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800140 }
141
142 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800143 * Returns the number of groups for the specified device in the store.
144 *
145 * @return number of groups for the specified device
146 */
147 @Override
148 public int getGroupCount(DeviceId deviceId) {
149 return (groupEntriesByKey.get(deviceId) != null) ?
150 groupEntriesByKey.get(deviceId).size() : 0;
151 }
152
153 /**
154 * Returns the groups associated with a device.
155 *
156 * @param deviceId the device ID
157 *
158 * @return the group entries
159 */
160 @Override
161 public Iterable<Group> getGroups(DeviceId deviceId) {
162 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800163 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700164 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800165 }
166
167 /**
168 * Returns the stored group entry.
169 *
170 * @param deviceId the device ID
171 * @param appCookie the group key
172 *
173 * @return a group associated with the key
174 */
175 @Override
176 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
177 return (groupEntriesByKey.get(deviceId) != null) ?
178 groupEntriesByKey.get(deviceId).get(appCookie) :
179 null;
180 }
181
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700182 @Override
183 public Group getGroup(DeviceId deviceId, GroupId groupId) {
184 return (groupEntriesById.get(deviceId) != null) ?
185 groupEntriesById.get(deviceId).get(groupId) :
186 null;
187 }
188
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800189 private int getFreeGroupIdValue(DeviceId deviceId) {
190 int freeId = groupIdGen.incrementAndGet();
191
192 while (true) {
193 Group existing = (
194 groupEntriesById.get(deviceId) != null) ?
195 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
196 null;
197 if (existing == null) {
198 existing = (
199 extraneousGroupEntriesById.get(deviceId) != null) ?
200 extraneousGroupEntriesById.get(deviceId).
201 get(new DefaultGroupId(freeId)) :
202 null;
203 }
204 if (existing != null) {
205 freeId = groupIdGen.incrementAndGet();
206 } else {
207 break;
208 }
209 }
210 return freeId;
211 }
212
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800213 /**
214 * Stores a new group entry using the information from group description.
215 *
216 * @param groupDesc group description to be used to create group entry
217 */
218 @Override
219 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800220 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800221 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
222 return;
223 }
224
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800225 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
226 // Device group audit has not completed yet
227 // Add this group description to pending group key table
228 // Create a group entry object with Dummy Group ID
229 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
230 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
231 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
232 getPendingGroupKeyTable(groupDesc.deviceId());
233 pendingKeyTable.put(groupDesc.appCookie(), group);
234 return;
235 }
236
237 storeGroupDescriptionInternal(groupDesc);
238 }
239
240 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800241 // Check if a group is existing with the same key
242 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
243 return;
244 }
245
Saurav Das100e3b82015-04-30 11:12:10 -0700246 GroupId id = null;
247 if (groupDesc.givenGroupId() == null) {
248 // Get a new group identifier
249 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
250 } else {
251 id = new DefaultGroupId(groupDesc.givenGroupId());
252 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800253 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800254 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800255 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800256 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
257 getGroupKeyTable(groupDesc.deviceId());
258 keyTable.put(groupDesc.appCookie(), group);
259 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
260 getGroupIdTable(groupDesc.deviceId());
261 idTable.put(id, group);
262 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
263 group));
264 }
265
266 /**
267 * Updates the existing group entry with the information
268 * from group description.
269 *
270 * @param deviceId the device ID
271 * @param oldAppCookie the current group key
272 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800273 * @param newBuckets group buckets for updates
274 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800275 */
276 @Override
277 public void updateGroupDescription(DeviceId deviceId,
278 GroupKey oldAppCookie,
279 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800280 GroupBuckets newBuckets,
281 GroupKey newAppCookie) {
282 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800283 Group oldGroup = getGroup(deviceId, oldAppCookie);
284 if (oldGroup == null) {
285 return;
286 }
287
288 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
289 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800292 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800293 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800294 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800295 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
296 oldGroup.deviceId(),
297 oldGroup.type(),
298 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800299 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700300 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800301 oldGroup.appId());
302 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
303 updatedGroupDesc);
304 newGroup.setState(GroupState.PENDING_UPDATE);
305 newGroup.setLife(oldGroup.life());
306 newGroup.setPackets(oldGroup.packets());
307 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800309 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
310 getGroupKeyTable(oldGroup.deviceId());
311 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
312 getGroupIdTable(oldGroup.deviceId());
313 keyTable.remove(oldGroup.appCookie());
314 idTable.remove(oldGroup.id());
315 keyTable.put(newGroup.appCookie(), newGroup);
316 idTable.put(newGroup.id(), newGroup);
317 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
318 }
319 }
320
321 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
Victor Silvadf1eeae2016-08-12 15:28:57 -0300322 UpdateType type,
323 GroupBuckets buckets) {
324 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()
416 .filter((existingBucket)->(existingBucket.equals(bucket)))
417 .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}