2 * Copyright (c) 2011,2012 Big Switch Networks, Inc.
4 * Licensed under the Eclipse Public License, Version 1.0 (the
5 * "License"); you may not use this file except in compliance with the
6 * License. You may obtain a copy of the License at
8 * http://www.eclipse.org/legal/epl-v10.html
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
13 * implied. See the License for the specific language governing
14 * permissions and limitations under the License.
16 * This file incorporates work covered by the following copyright and
19 * Originally created by David Erickson, Stanford University
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the
23 * License. You may obtain a copy of the License at
25 * http://www.apache.org/licenses/LICENSE-2.0
27 * Unless required by applicable law or agreed to in writing,
28 * software distributed under the License is distributed on an "AS
29 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
30 * express or implied. See the License for the specific language
31 * governing permissions and limitations under the License.
34 package org.opendaylight.controller.hosttracker.internal;
36 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.ADD;
37 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.CHANGE;
38 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.DELETE;
40 import java.util.ArrayList;
41 import java.util.Calendar;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.Date;
46 import java.util.EnumSet;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.Iterator;
50 import java.util.LinkedList;
51 import java.util.List;
53 import java.util.Queue;
55 import java.util.concurrent.ConcurrentHashMap;
56 import java.util.concurrent.Executors;
57 import java.util.concurrent.ScheduledExecutorService;
58 import java.util.concurrent.TimeUnit;
60 import org.opendaylight.controller.hosttracker.Entity;
61 import org.opendaylight.controller.hosttracker.IDevice;
62 import org.opendaylight.controller.hosttracker.IDeviceListener;
63 import org.opendaylight.controller.hosttracker.IDeviceService;
64 import org.opendaylight.controller.hosttracker.IEntityClass;
65 import org.opendaylight.controller.hosttracker.IEntityClassListener;
66 import org.opendaylight.controller.hosttracker.IEntityClassifierService;
67 import org.opendaylight.controller.hosttracker.SwitchPort;
68 import org.opendaylight.controller.sal.core.Edge;
69 import org.opendaylight.controller.sal.core.NodeConnector;
70 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
71 import org.opendaylight.controller.sal.packet.ARP;
72 import org.opendaylight.controller.sal.packet.Ethernet;
73 import org.opendaylight.controller.sal.packet.IDataPacketService;
74 import org.opendaylight.controller.sal.packet.IListenDataPacket;
75 import org.opendaylight.controller.sal.packet.Packet;
76 import org.opendaylight.controller.sal.packet.PacketResult;
77 import org.opendaylight.controller.sal.packet.RawPacket;
78 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
79 import org.opendaylight.controller.sal.utils.ListenerDispatcher;
80 import org.opendaylight.controller.sal.utils.MultiIterator;
81 import org.opendaylight.controller.sal.utils.SingletonTask;
82 import org.opendaylight.controller.switchmanager.ISwitchManager;
83 import org.opendaylight.controller.topologymanager.ITopologyManager;
84 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
89 * DeviceManager creates Devices based upon MAC addresses seen in the network.
90 * It tracks any network addresses mapped to the Device, and its location within
95 public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
96 IListenDataPacket, ITopologyManagerAware {
97 protected static Logger logger = LoggerFactory
98 .getLogger(DeviceManagerImpl.class);
100 public static final String MODULE_NAME = "devicemanager";
102 // protected ITopologyService topology;
103 // protected IStorageSourceService storageSource;
104 // protected IRestApiService restApi;
105 // protected IThreadPoolService threadPool;
106 // protected IFlowReconcileService flowReconcileMgr;
107 // protected IFlowReconcileEngineService flowReconcileEngine;
108 // protected IDebugCounterService debugCounters;
109 // private ISyncService syncService;
110 // private IStoreClient<String,DeviceSyncRepresentation> storeClient;
111 // private DeviceSyncManager deviceSyncManager;
113 private ITopologyManager topology;
114 private ISwitchManager switchManager = null;
115 private IDataPacketService dataPacketService = null;
117 public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
118 public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
119 + "-reconcileRequest";
120 public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
121 + "-reconcileNoSourceDevice";
122 public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
123 + "-reconcileNoDestDevice";
124 public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
125 + "-broadcastSource";
126 public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
127 public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
128 public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
129 + "-dhcpClientNameSnooped";
130 public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
131 + "-deviceOnInternalPortNotLearned";
132 public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
133 + "-packetNotAllowed";
134 public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
135 public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
136 + "-packetOnInternalPortForKnownDevice";
137 public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
138 public static final String CNT_DEVICE_CHANGED = MODULE_NAME
140 public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
141 public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
142 + "-cleanupEntitiesRuns";
143 public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
144 + "-entityRemovedTimeout";
145 public static final String CNT_DEVICE_DELETED = MODULE_NAME
147 public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
148 + "-deviceReclassifyDelete";
149 public static final String CNT_DEVICE_STORED = MODULE_NAME
151 public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
152 + "-deviceStoreThrottled";
153 public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
154 + "-deviceRemovedFromStore";
155 public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
157 public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
158 + "-devicesFromStore";
159 public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
160 + "-consolidateStoreRuns";
161 public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
162 + "-consolidateStoreDevicesRemoved";
164 static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
165 .getCanonicalName() + ".stateStore";
168 * Time interval between writes of entries for the same device to the sync
171 // static final int DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS =
172 // 5*60*1000; // 5 min
173 // private int syncStoreWriteIntervalMs =
174 // DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS;
177 * Time after SLAVE->MASTER until we run the consolidate store code.
179 // static final int DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS =
180 // 15*1000; // 15 sec
181 // private int initialSyncStoreConsolidateMs =
182 // DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS;
185 * Time interval between consolidate store runs.
187 // static final int DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS =
188 // 75*60*1000; // 75 min
189 // private final int syncStoreConsolidateIntervalMs =
190 // DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS;
193 * Time in milliseconds before entities will expire
195 protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
198 * Time in seconds between cleaning up old entities/devices
200 protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
203 * This is the master device map that maps device IDs to {@link Device}
206 protected ConcurrentHashMap<Long, Device> deviceMap;
209 * Counter used to generate device keys
211 protected long deviceKeyCounter = 0;
214 * Lock for incrementing the device key counter
216 protected Object deviceKeyLock = new Object();
219 * This is the primary entity index that contains all entities
221 protected DeviceUniqueIndex primaryIndex;
224 * This stores secondary indices over the fields in the devices
226 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
229 * This map contains state for each of the {@ref IEntityClass} that exist
231 protected ConcurrentHashMap<String, ClassState> classStateMap;
234 * This is the list of indices we want on a per-class basis
236 protected Set<EnumSet<DeviceField>> perClassIndices;
239 * The entity classifier currently in use
241 protected IEntityClassifierService entityClassifier;
244 * Used to cache state about specific entity classes
246 protected class ClassState {
251 protected DeviceUniqueIndex classIndex;
254 * This stores secondary indices over the fields in the device for the
257 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
260 * Allocate a new {@link ClassState} object for the class
263 * the class to use for the state
265 public ClassState(IEntityClass clazz) {
266 EnumSet<DeviceField> keyFields = clazz.getKeyFields();
267 EnumSet<DeviceField> primaryKeyFields = entityClassifier
269 boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
271 if (!keyFieldsMatchPrimary)
272 classIndex = new DeviceUniqueIndex(keyFields);
274 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
275 for (EnumSet<DeviceField> fields : perClassIndices) {
276 secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
282 * Device manager event listeners reclassifyDeviceListeners are notified
283 * first before reconcileDeviceListeners. This is to make sure devices are
284 * correctly reclassified before reconciliation.
286 protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
289 * A device update event to be dispatched
291 protected static class DeviceUpdate {
297 * The affected device
299 protected Device device;
302 * The change that was made
304 protected Change change;
307 * If not added, then this is the list of fields changed
309 protected EnumSet<DeviceField> fieldsChanged;
311 public DeviceUpdate(Device device, Change change,
312 EnumSet<DeviceField> fieldsChanged) {
314 this.device = device;
315 this.change = change;
316 this.fieldsChanged = fieldsChanged;
320 public String toString() {
321 String devIdStr = device.getEntityClass().getName() + "::"
322 + device.getMACAddressString();
323 return "DeviceUpdate [device=" + devIdStr + ", change=" + change
324 + ", fieldsChanged=" + fieldsChanged + "]";
330 * AttachmentPointComparator
332 * Compares two attachment points and returns the latest one. It is assumed
333 * that the two attachment points are in the same L2 domain.
337 protected class AttachmentPointComparator implements
338 Comparator<AttachmentPoint> {
339 public AttachmentPointComparator() {
344 public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
345 // First compare based on L2 domain ID;
347 // XXX - missing functionality -- need topology
348 // long oldDomain = topology.getL2DomainId(oldSw);
349 // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
351 boolean oldBD = false;
353 // XXX - missing functionality -- need topology
354 // long newDomain = topology.getL2DomainId(newSw);
355 // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
357 boolean newBD = false;
359 if (oldDomain < newDomain)
361 else if (oldDomain > newDomain)
364 // Give preference to OFPP_LOCAL always
365 if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
366 && newAP.getPort().getType()
367 .equals(NodeConnectorIDType.SWSTACK)) {
369 } else if (oldAP.getPort().getType()
370 .equals(NodeConnectorIDType.SWSTACK)
371 && !newAP.getPort().getType()
372 .equals(NodeConnectorIDType.SWSTACK)) {
376 // We expect that the last seen of the new AP is higher than
377 // old AP, if it is not, just reverse and send the negative
379 if (oldAP.getActiveSince() > newAP.getActiveSince())
380 return -compare(newAP, oldAP);
382 long activeOffset = 0;
383 // XXX - missing functionality -- need topology
384 // if (!topology.isConsistent(oldSw, oldPort, newSw, newPort)) {
385 if (!newBD && oldBD) {
388 if (newBD && oldBD) {
389 activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
390 } else if (newBD && !oldBD) {
391 activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
395 // // The attachment point is consistent.
396 // activeOffset = AttachmentPoint.CONSISTENT_TIMEOUT;
399 if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
400 || (newAP.getLastSeen() > oldAP.getLastSeen()
401 + AttachmentPoint.INACTIVITY_INTERVAL)) {
409 * Comparator for sorting by cluster ID
411 public AttachmentPointComparator apComparator;
414 * Switch ports where attachment points shouldn't be learned
416 private Set<SwitchPort> suppressAPs;
419 * Periodic task to clean up expired entities
421 public SingletonTask entityCleanupTask;
423 // ********************
424 // Dependency injection
425 // ********************
427 void setDataPacketService(IDataPacketService s) {
428 this.dataPacketService = s;
431 void unsetDataPacketService(IDataPacketService s) {
432 if (this.dataPacketService == s) {
433 this.dataPacketService = null;
437 public void setTopologyManager(ITopologyManager s) {
441 public void unsetTopologyManager(ITopologyManager s) {
442 if (this.topology == s) {
443 logger.debug("Topology Manager Service removed!");
444 this.topology = null;
448 private volatile boolean stopped = true;
449 private ScheduledExecutorService ses;
457 public void start() {
458 this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
460 // XXX - TODO need to make it possible to register a non-default
462 entityClassifier = new DefaultEntityClassifier();
463 this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
464 this.suppressAPs = Collections
465 .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
466 primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
467 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
469 deviceMap = new ConcurrentHashMap<Long, Device>();
470 classStateMap = new ConcurrentHashMap<String, ClassState>();
471 apComparator = new AttachmentPointComparator();
473 addIndex(true, EnumSet.of(DeviceField.IPV4));
475 // floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
476 // floodlightProvider.addHAListener(this.haListenerDelegate);
477 // if (topology != null)
478 // topology.addListener(this);
479 // flowReconcileMgr.addFlowReconcileListener(this);
480 // entityClassifier.addListener(this);
483 // XXX - Should use a common threadpool but this doesn't currently exist
484 ses = Executors.newScheduledThreadPool(1);
485 Runnable ecr = new Runnable() {
490 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
494 entityCleanupTask = new SingletonTask(ses, ecr);
495 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
498 * XXX Missing functionality if (restApi != null) {
499 * restApi.addRestletRoutable(new DeviceRoutable()); } else {
500 * logger.debug("Could not instantiate REST API"); }
503 registerDeviceManagerDebugCounters();
506 * XXX Missing functionality try {
507 * this.syncService.registerStore(DEVICE_SYNC_STORE_NAME, Scope.LOCAL);
508 * this.storeClient = this.syncService
509 * .getStoreClient(DEVICE_SYNC_STORE_NAME, String.class,
510 * DeviceSyncRepresentation.class); } catch (SyncException e) { throw
511 * new FloodlightModuleException("Error while setting up sync service",
514 * Runnable consolidateStoreRunner = new Runnable() {
516 * @Override public void run() { deviceSyncManager.consolidateStore();
517 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
518 * TimeUnit.MILLISECONDS); debugCounters.flushCounters(); } };
519 * storeConsolidateTask = new SingletonTask(ses,
520 * consolidateStoreRunner); if (isMaster)
521 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
522 * TimeUnit.MILLISECONDS);
527 * Periodic task to consolidate entries in the store. I.e., delete entries
528 * in the store that are not known to DeviceManager
530 // XXX - Missing functionality
531 // private SingletonTask storeConsolidateTask;
533 // *********************
534 // IDeviceManagerService
535 // *********************
538 public IDevice getDevice(Long deviceKey) {
539 return deviceMap.get(deviceKey);
543 public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
544 NodeConnector port) throws IllegalArgumentException {
545 if (vlan != null && vlan.shortValue() <= 0)
547 if (ipv4Address != null && ipv4Address == 0)
549 Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
550 if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
551 throw new IllegalArgumentException("Not all key fields specified."
552 + " Required fields: " + entityClassifier.getKeyFields());
554 return findDeviceByEntity(e);
558 public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
559 Short vlan, Integer ipv4Address) throws IllegalArgumentException {
560 if (vlan != null && vlan.shortValue() <= 0)
562 if (ipv4Address != null && ipv4Address == 0)
564 Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
565 if (entityClass == null
566 || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
567 throw new IllegalArgumentException("Not all key fields and/or "
568 + " no source device specified. Required fields: "
569 + entityClassifier.getKeyFields());
571 return findDestByEntity(entityClass, e);
575 public Collection<? extends IDevice> getAllDevices() {
576 return Collections.unmodifiableCollection(deviceMap.values());
580 public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
582 perClassIndices.add(keyFields);
584 secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
589 public Iterator<? extends IDevice> queryDevices(Long macAddress,
590 Short vlan, Integer ipv4Address, NodeConnector port) {
591 DeviceIndex index = null;
592 if (secondaryIndexMap.size() > 0) {
593 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
595 index = secondaryIndexMap.get(keys);
598 Iterator<Device> deviceIterator = null;
600 // Do a full table scan
601 deviceIterator = deviceMap.values().iterator();
604 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
605 vlan, ipv4Address, port, null);
606 deviceIterator = new DeviceIndexInterator(this,
607 index.queryByEntity(entity));
610 DeviceIterator di = new DeviceIterator(deviceIterator, null,
611 macAddress, vlan, ipv4Address, port);
616 public Iterator<? extends IDevice> queryClassDevices(
617 IEntityClass entityClass, Long macAddress, Short vlan,
618 Integer ipv4Address, NodeConnector port) {
619 ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
620 ClassState classState = getClassState(entityClass);
622 DeviceIndex index = null;
623 if (classState.secondaryIndexMap.size() > 0) {
624 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
626 index = classState.secondaryIndexMap.get(keys);
629 Iterator<Device> iter;
631 index = classState.classIndex;
634 return new DeviceIterator(deviceMap.values().iterator(),
635 new IEntityClass[] { entityClass }, macAddress, vlan,
638 // scan the entire class
639 iter = new DeviceIndexInterator(this, index.getAll());
643 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
644 vlan, ipv4Address, port, null);
645 iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
649 return new MultiIterator<Device>(iterators.iterator());
652 protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
653 Short vlan, Integer ipv4Address, NodeConnector port) {
654 DeviceIndex index = null;
655 if (secondaryIndexMap.size() > 0) {
656 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
658 index = secondaryIndexMap.get(keys);
661 Iterator<Device> deviceIterator = null;
663 // Do a full table scan
664 deviceIterator = deviceMap.values().iterator();
667 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
668 vlan, ipv4Address, port, null);
669 deviceIterator = new DeviceIndexInterator(this,
670 index.queryByEntity(entity));
673 DeviceIterator di = new DeviceIterator(deviceIterator, null,
674 macAddress, vlan, ipv4Address, port);
679 public void addListener(IDeviceListener listener) {
680 deviceListeners.addListener("device", listener);
685 public void addSuppressAPs(NodeConnector port) {
686 suppressAPs.add(new SwitchPort(port));
690 public void removeSuppressAPs(NodeConnector port) {
691 suppressAPs.remove(new SwitchPort(port));
695 public Set<SwitchPort> getSuppressAPs() {
696 return Collections.unmodifiableSet(suppressAPs);
699 private void logListeners() {
700 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
701 if (listeners != null) {
702 StringBuffer sb = new StringBuffer();
703 sb.append("DeviceListeners: ");
704 for (IDeviceListener l : listeners) {
705 sb.append(l.getName());
708 logger.debug(sb.toString());
713 // IFlowReconcileListener
716 * XXX - Missing functionality
718 * @Override public Command reconcileFlows(ArrayList<OFMatchReconcile>
719 * ofmRcList) { ListIterator<OFMatchReconcile> iter =
720 * ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm =
723 * // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) {
726 * if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { return
729 * protected Command reconcileFlow(OFMatchReconcile ofm) {
730 * debugCounters.updateCounter(CNT_RECONCILE_REQUEST); // Extract source
731 * entity information Entity srcEntity =
732 * getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) {
733 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
736 * // Find the device by source entity Device srcDevice =
737 * findDeviceByEntity(srcEntity); if (srcDevice == null) {
738 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
739 * Command.STOP; } // Store the source device in the context
740 * fcStore.put(ofm.cntx, CONTEXT_SRC_DEVICE, srcDevice);
742 * // Find the device matching the destination from the entity // classes of
743 * the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid,
744 * false); Device dstDevice = null; if (dstEntity != null) { dstDevice =
745 * findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice !=
746 * null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else
747 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } else {
748 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } if
749 * (logger.isTraceEnabled()) {
750 * logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " +
751 * "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
752 * srcEntity, srcDevice, dstEntity, dstDevice } ); } return
753 * Command.CONTINUE; }
761 public PacketResult receiveDataPacket(RawPacket inPkt) {
762 // XXX - Can this really pass in null? Why would you ever want that?
764 return PacketResult.IGNORED;
767 throw new Exception("Sample");
768 } catch (Exception e) {
769 logger.error("Sample stack trace", e);
772 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
774 if (formattedPak instanceof Ethernet) {
775 eth = (Ethernet) formattedPak;
777 return PacketResult.IGNORED;
780 // Extract source entity information
781 NodeConnector inPort = inPkt.getIncomingNodeConnector();
782 Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
783 if (srcEntity == null) {
784 // debugCounters.updateCounter(CNT_BROADCAST_SOURCE);
785 return PacketResult.CONSUME;
788 // Learn from ARP packet for special VRRP settings.
789 // In VRRP settings, the source MAC address and sender MAC
790 // addresses can be different. In such cases, we need to learn
791 // the IP to MAC mapping of the VRRP IP address. The source
792 // entity will not have that information. Hence, a separate call
793 // to learn devices in such cases.
794 learnDeviceFromArpResponseData(eth, inPort);
796 // Learn/lookup device information
797 Device srcDevice = learnDeviceByEntity(srcEntity);
798 if (srcDevice == null) {
799 // debugCounters.updateCounter(CNT_NO_SOURCE);
800 return PacketResult.CONSUME;
802 logger.trace("Saw packet from device {}", srcDevice);
804 // // Store the source device in the context
805 // fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice);
807 // // Find the device matching the destination from the entity
808 // // classes of the source.
809 // Entity dstEntity = getDestEntityFromPacket(eth);
810 // Device dstDevice = null;
811 // if (dstEntity != null) {
813 // findDestByEntity(srcDevice.getEntityClass(), dstEntity);
814 // if (dstDevice != null)
815 // fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice);
817 // //debugCounters.updateCounter(CNT_NO_DEST);
819 // //debugCounters.updateCounter(CNT_NO_DEST);
822 // if (logger.isTraceEnabled()) {
823 // logger.trace("Received PI: {} on switch {}, port {} *** eth={}" +
824 // " *** srcDev={} *** dstDev={} *** ",
825 // new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
826 // srcDevice, dstDevice });
829 // snoopDHCPClientName(eth, srcDevice);
831 return PacketResult.KEEP_PROCESSING;
839 * Snoop and record client-provided host name from DHCP requests
844 // private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
845 // if (! (eth.getPayload() instanceof IPv4) )
847 // IPv4 ipv4 = (IPv4) eth.getPayload();
848 // if (! (ipv4.getPayload() instanceof UDP) )
850 // UDP udp = (UDP) ipv4.getPayload();
851 // if (!(udp.getPayload() instanceof DHCP))
853 // DHCP dhcp = (DHCP) udp.getPayload();
854 // byte opcode = dhcp.getOpCode();
855 // if (opcode == DHCP.OPCODE_REQUEST) {
856 // DHCPOption dhcpOption = dhcp.getOption(
857 // DHCPOptionCode.OptionCode_Hostname);
858 // if (dhcpOption != null) {
859 // debugCounters.updateCounter(CNT_DHCP_CLIENT_NAME_SNOOPED);
860 // srcDevice.dhcpClientName = new String(dhcpOption.getData());
866 * Check whether the given attachment point is valid given the current
873 * @return true if it's a valid attachment point
875 public boolean isValidAttachmentPoint(NodeConnector port) {
876 // XXX - missing functionality -- need topology module
877 // if (topology.isAttachmentPointPort(port) == false)
879 if (topology.isInternal(port))
881 if (!switchManager.isNodeConnectorEnabled(port))
883 if (suppressAPs.contains(new SwitchPort(port)))
890 * Get sender IP address from packet if the packet is either an ARP packet.
896 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
897 if (eth.getPayload() instanceof ARP) {
898 ARP arp = (ARP) eth.getPayload();
899 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
900 && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
901 return toIPv4Address(arp.getSenderProtocolAddress());
908 * Parse an entity from an {@link Ethernet} packet.
911 * the packet to parse
913 * the switch on which the packet arrived
915 * the original packetin
916 * @return the entity from the packet
918 protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
919 byte[] dlAddrArr = eth.getSourceMACAddress();
920 long dlAddr = toLong(dlAddrArr);
922 // Ignore broadcast/multicast source
923 if ((dlAddrArr[0] & 0x1) != 0)
926 // XXX missing functionality
928 int nwSrc = getSrcNwAddr(eth, dlAddr);
929 return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
934 * Learn device from ARP data in scenarios where the Ethernet source MAC is
935 * different from the sender hardware address in ARP data.
937 protected void learnDeviceFromArpResponseData(Ethernet eth,
938 NodeConnector port) {
940 if (!(eth.getPayload() instanceof ARP))
942 ARP arp = (ARP) eth.getPayload();
944 byte[] dlAddrArr = eth.getSourceMACAddress();
945 long dlAddr = toLong(dlAddrArr);
947 byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
948 long senderAddr = toLong(senderHardwareAddr);
950 if (dlAddr == senderAddr)
953 // Ignore broadcast/multicast source
954 if ((senderHardwareAddr[0] & 0x1) != 0)
957 // short vlan = eth.getVlanID();
958 int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
960 Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
963 learnDeviceByEntity(e);
967 * Get a (partial) entity for the destination from the packet.
972 // protected Entity getDestEntityFromPacket(Ethernet eth) {
973 // byte[] dlAddrArr = eth.getDestinationMACAddress();
974 // long dlAddr = Ethernet.toLong(dlAddrArr);
975 // short vlan = eth.getVlanID();
978 // // Ignore broadcast/multicast destination
979 // if ((dlAddrArr[0] & 0x1) != 0)
982 // if (eth.getPayload() instanceof IPv4) {
983 // IPv4 ipv4 = (IPv4) eth.getPayload();
984 // nwDst = ipv4.getDestinationAddress();
987 // return new Entity(dlAddr,
988 // ((vlan >= 0) ? vlan : null),
989 // ((nwDst != 0) ? nwDst : null),
996 * Parse an entity from an OFMatchWithSwDpid.
998 * @param ofmWithSwDpid
999 * @return the entity from the packet
1001 // private Entity getEntityFromFlowMod(OFMatchWithSwDpid ofmWithSwDpid,
1002 // boolean isSource) {
1003 // byte[] dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerSource();
1004 // int nwSrc = ofmWithSwDpid.getOfMatch().getNetworkSource();
1006 // dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerDestination();
1007 // nwSrc = ofmWithSwDpid.getOfMatch().getNetworkDestination();
1010 // long dlAddr = Ethernet.toLong(dlAddrArr);
1012 // // Ignore broadcast/multicast source
1013 // if ((dlAddrArr[0] & 0x1) != 0)
1016 // Long swDpid = null;
1017 // Short inPort = null;
1020 // swDpid = ofmWithSwDpid.getSwitchDataPathId();
1021 // inPort = ofmWithSwDpid.getOfMatch().getInputPort();
1024 // /**for the new flow cache design, the flow mods retrived are not always
1025 // from the source, learn AP should be disabled --meiyang*/
1026 // boolean learnap = false;
1028 // * if (swDpid == null ||
1029 // inPort == null ||
1030 // !isValidAttachmentPoint(swDpid, inPort)) {
1031 // // If this is an internal port or we otherwise don't want
1032 // // to learn on these ports. In the future, we should
1033 // // handle this case by labeling flows with something that
1034 // // will give us the entity class. For now, we'll do our
1035 // // best assuming attachment point information isn't used
1036 // // as a key field.
1041 // short vlan = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLan();
1042 // return new Entity(dlAddr,
1043 // ((vlan >= 0) ? vlan : null),
1044 // ((nwSrc != 0) ? nwSrc : null),
1045 // (learnap ? swDpid : null),
1046 // (learnap ? (int)inPort : null),
1051 * Look up a {@link Device} based on the provided {@link Entity}. We first
1052 * check the primary index. If we do not find an entry there we classify the
1053 * device into its IEntityClass and query the classIndex. This implies that
1054 * all key field of the current IEntityClassifier must be present in the
1055 * entity for the lookup to succeed!
1058 * the entity to search for
1059 * @return The {@link Device} object if found
1061 protected Device findDeviceByEntity(Entity entity) {
1062 // Look up the fully-qualified entity to see if it already
1063 // exists in the primary entity index.
1064 Long deviceKey = primaryIndex.findByEntity(entity);
1065 IEntityClass entityClass = null;
1067 if (deviceKey == null) {
1068 // If the entity does not exist in the primary entity index,
1069 // use the entity classifier for find the classes for the
1070 // entity. Look up the entity in the returned class'
1071 // class entity index.
1072 entityClass = entityClassifier.classifyEntity(entity);
1073 if (entityClass == null) {
1076 ClassState classState = getClassState(entityClass);
1078 if (classState.classIndex != null) {
1079 deviceKey = classState.classIndex.findByEntity(entity);
1082 if (deviceKey == null)
1084 return deviceMap.get(deviceKey);
1088 * Get a destination device using entity fields that corresponds with the
1089 * given source device. The source device is important since there could be
1090 * ambiguity in the destination device without the attachment point
1094 * the source device's entity class. The returned destination
1095 * will be in the same entity class as the source.
1097 * the entity to look up
1098 * @return an {@link Device} or null if no device is found.
1100 protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
1102 // Look up the fully-qualified entity to see if it
1103 // exists in the primary entity index
1104 Long deviceKey = primaryIndex.findByEntity(dstEntity);
1106 if (deviceKey == null) {
1107 // This could happen because:
1108 // 1) no destination known, or a broadcast destination
1109 // 2) if we have attachment point key fields since
1110 // attachment point information isn't available for
1111 // destination devices.
1112 // For the second case, we'll need to match up the
1113 // destination device with the class of the source
1115 ClassState classState = getClassState(reference);
1116 if (classState.classIndex == null) {
1119 deviceKey = classState.classIndex.findByEntity(dstEntity);
1121 if (deviceKey == null)
1123 return deviceMap.get(deviceKey);
1127 * Look up a {@link Device} within a particular entity class based on the
1128 * provided {@link Entity}.
1131 * the entity class to search for the entity
1133 * the entity to search for
1134 * @return The {@link Device} object if found private Device
1135 * findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
1136 * XXX - TODO throw new UnsupportedOperationException(); }
1140 * Look up a {@link Device} based on the provided {@link Entity}. Also
1141 * learns based on the new entity, and will update existing devices as
1145 * the {@link Entity}
1146 * @return The {@link Device} object if found
1148 protected Device learnDeviceByEntity(Entity entity) {
1149 logger.info("Primary index {}", primaryIndex);
1150 ArrayList<Long> deleteQueue = null;
1151 LinkedList<DeviceUpdate> deviceUpdates = null;
1152 Device device = null;
1154 // we may need to restart the learning process if we detect
1155 // concurrent modification. Note that we ensure that at least
1156 // one thread should always succeed so we don't get into infinite
1159 deviceUpdates = null;
1161 // Look up the fully-qualified entity to see if it already
1162 // exists in the primary entity index.
1163 Long deviceKey = primaryIndex.findByEntity(entity);
1164 IEntityClass entityClass = null;
1166 if (deviceKey == null) {
1167 // If the entity does not exist in the primary entity index,
1168 // use the entity classifier for find the classes for the
1169 // entity. Look up the entity in the returned class'
1170 // class entity index.
1171 entityClass = entityClassifier.classifyEntity(entity);
1172 if (entityClass == null) {
1173 // could not classify entity. No device
1177 ClassState classState = getClassState(entityClass);
1179 if (classState.classIndex != null) {
1180 deviceKey = classState.classIndex.findByEntity(entity);
1183 if (deviceKey != null) {
1184 // If the primary or secondary index contains the entity
1185 // use resulting device key to look up the device in the
1186 // device map, and use the referenced Device below.
1187 device = deviceMap.get(deviceKey);
1188 if (device == null) {
1189 // This can happen due to concurrent modification
1190 if (logger.isDebugEnabled()) {
1191 logger.debug("No device for deviceKey {} while "
1192 + "while processing entity {}", deviceKey,
1195 // if so, then try again till we don't even get the device
1197 // and so we recreate the device
1201 // If the secondary index does not contain the entity,
1202 // create a new Device object containing the entity, and
1203 // generate a new device ID if the the entity is on an
1204 // attachment point port. Otherwise ignore.
1205 if (entity.hasSwitchPort()
1206 && !isValidAttachmentPoint(entity.getPort())) {
1207 // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
1208 if (logger.isDebugEnabled()) {
1209 logger.debug("Not learning new device on internal"
1210 + " link: {}", entity);
1215 // Before we create the new device also check if
1216 // the entity is allowed (e.g., for spoofing protection)
1217 if (!isEntityAllowed(entity, entityClass)) {
1218 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1219 if (logger.isDebugEnabled()) {
1220 logger.debug("PacketIn is not allowed {} {}",
1221 entityClass.getName(), entity);
1226 synchronized (deviceKeyLock) {
1227 deviceKey = Long.valueOf(deviceKeyCounter++);
1229 device = allocateDevice(deviceKey, entity, entityClass);
1231 // Add the new device to the primary map with a simple put
1232 deviceMap.put(deviceKey, device);
1235 if (!updateIndices(device, deviceKey)) {
1236 if (deleteQueue == null)
1237 deleteQueue = new ArrayList<Long>();
1238 deleteQueue.add(deviceKey);
1242 updateSecondaryIndices(entity, entityClass, deviceKey);
1244 // We need to count and log here. If we log earlier we could
1245 // hit a concurrent modification and restart the dev creation
1246 // and potentially count the device twice.
1247 // debugCounters.updateCounter(CNT_NEW_DEVICE);
1248 if (logger.isDebugEnabled()) {
1250 "New device created: {} deviceKey={}, entity={}",
1251 new Object[] { device, deviceKey, entity });
1253 // generate new device update
1254 deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
1255 device, ADD, null));
1259 // if it gets here, we have a pre-existing Device for this Entity
1260 if (!isEntityAllowed(entity, device.getEntityClass())) {
1261 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1262 if (logger.isDebugEnabled()) {
1263 logger.info("PacketIn is not allowed {} {}", device
1264 .getEntityClass().getName(), entity);
1268 // If this is not an attachment point port we don't learn the new
1270 // and don't update indexes. But we do allow the device to continue
1273 if (entity.hasSwitchPort()
1274 && !isValidAttachmentPoint(entity.getPort())) {
1275 // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
1278 int entityindex = -1;
1279 if ((entityindex = device.entityIndex(entity)) >= 0) {
1280 // Entity already exists
1281 // update timestamp on the found entity
1282 Date lastSeen = entity.getLastSeenTimestamp();
1283 if (lastSeen == null) {
1284 lastSeen = new Date();
1285 entity.setLastSeenTimestamp(lastSeen);
1287 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
1288 // we break the loop after checking for changes to the AP
1290 // New entity for this device
1291 // compute the insertion point for the entity.
1292 // see Arrays.binarySearch()
1293 entityindex = -(entityindex + 1);
1294 Device newDevice = allocateDevice(device, entity, entityindex);
1297 EnumSet<DeviceField> changedFields = findChangedFields(device,
1300 // update the device map with a replace call
1301 boolean res = deviceMap.replace(deviceKey, device, newDevice);
1302 // If replace returns false, restart the process from the
1303 // beginning (this implies another thread concurrently
1304 // modified this Device).
1310 if (!updateIndices(device, deviceKey)) {
1313 updateSecondaryIndices(entity, device.getEntityClass(),
1316 // We need to count here after all the possible "continue"
1317 // statements in this branch
1318 // debugCounters.updateCounter(CNT_NEW_ENTITY);
1319 if (changedFields.size() > 0) {
1320 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1321 deviceUpdates = updateUpdates(deviceUpdates,
1322 new DeviceUpdate(newDevice, CHANGE, changedFields));
1324 // we break the loop after checking for changed AP
1326 // Update attachment point (will only be hit if the device
1327 // already existed and no concurrent modification)
1328 if (entity.hasSwitchPort()) {
1329 boolean moved = device.updateAttachmentPoint(entity.getPort(),
1330 entity.getLastSeenTimestamp().getTime());
1331 // TODO: use update mechanism instead of sending the
1332 // notification directly
1334 // we count device moved events in
1335 // sendDeviceMovedNotification()
1336 sendDeviceMovedNotification(device);
1337 if (logger.isTraceEnabled()) {
1338 logger.trace("Device moved: attachment points {},"
1339 + "entities {}", device.attachmentPoints,
1343 if (logger.isTraceEnabled()) {
1344 logger.trace("Device attachment point updated: "
1345 + "attachment points {}," + "entities {}",
1346 device.attachmentPoints, device.entities);
1353 if (deleteQueue != null) {
1354 for (Long l : deleteQueue) {
1355 Device dev = deviceMap.get(l);
1356 this.deleteDevice(dev);
1360 processUpdates(deviceUpdates);
1361 // deviceSyncManager.storeDeviceThrottled(device);
1366 protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
1370 protected EnumSet<DeviceField> findChangedFields(Device device,
1372 EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
1373 DeviceField.VLAN, DeviceField.SWITCHPORT);
1375 if (newEntity.getIpv4Address() == null)
1376 changedFields.remove(DeviceField.IPV4);
1377 if (newEntity.getVlan() == null)
1378 changedFields.remove(DeviceField.VLAN);
1379 if (newEntity.getPort() == null)
1380 changedFields.remove(DeviceField.SWITCHPORT);
1382 if (changedFields.size() == 0)
1383 return changedFields;
1385 for (Entity entity : device.getEntities()) {
1386 if (newEntity.getIpv4Address() == null
1387 || (entity.getIpv4Address() != null && entity
1389 .equals(newEntity.getIpv4Address())))
1390 changedFields.remove(DeviceField.IPV4);
1391 if (newEntity.getVlan() == null
1392 || (entity.getVlan() != null && entity.getVlan().equals(
1393 newEntity.getVlan())))
1394 changedFields.remove(DeviceField.VLAN);
1395 if (newEntity.getPort() == null
1396 || (entity.getPort() != null && entity.getPort().equals(
1397 newEntity.getPort())))
1398 changedFields.remove(DeviceField.SWITCHPORT);
1401 return changedFields;
1405 * Send update notifications to listeners
1408 * the updates to process.
1410 protected void processUpdates(Queue<DeviceUpdate> updates) {
1411 if (updates == null)
1413 DeviceUpdate update = null;
1414 while (null != (update = updates.poll())) {
1415 if (logger.isTraceEnabled()) {
1416 logger.trace("Dispatching device update: {}", update);
1418 // if (update.change == DeviceUpdate.Change.DELETE)
1419 // deviceSyncManager.removeDevice(update.device);
1421 // deviceSyncManager.storeDevice(update.device);
1422 List<IDeviceListener> listeners = deviceListeners
1423 .getOrderedListeners();
1424 notifyListeners(listeners, update);
1428 protected void notifyListeners(List<IDeviceListener> listeners,
1429 DeviceUpdate update) {
1430 if (listeners == null) {
1433 for (IDeviceListener listener : listeners) {
1434 switch (update.change) {
1436 listener.deviceAdded(update.device);
1439 listener.deviceRemoved(update.device);
1442 for (DeviceField field : update.fieldsChanged) {
1445 listener.deviceIPV4AddrChanged(update.device);
1448 // listener.deviceMoved(update.device);
1451 listener.deviceVlanChanged(update.device);
1454 logger.debug("Unknown device field changed {}",
1455 update.fieldsChanged.toString());
1465 * Check if the entity e has all the keyFields set. Returns false if not
1470 * the key fields to check e against
1473 protected boolean allKeyFieldsPresent(Entity e,
1474 EnumSet<DeviceField> keyFields) {
1475 for (DeviceField f : keyFields) {
1478 // MAC address is always present
1481 if (e.getIpv4Address() == null)
1485 if (e.getPort() == null)
1489 // FIXME: vlan==null is ambiguous: it can mean: not present
1491 // if (e.vlan == null) return false;
1494 // we should never get here. unless somebody extended
1496 throw new IllegalStateException();
1502 private LinkedList<DeviceUpdate> updateUpdates(
1503 LinkedList<DeviceUpdate> list, DeviceUpdate update) {
1507 list = new LinkedList<DeviceUpdate>();
1514 * Get the secondary index for a class. Will return null if the secondary
1515 * index was created concurrently in another thread.
1518 * the class for the index
1521 private ClassState getClassState(IEntityClass clazz) {
1522 ClassState classState = classStateMap.get(clazz.getName());
1523 if (classState != null)
1526 classState = new ClassState(clazz);
1527 ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
1536 * Update both the primary and class indices for the provided device. If the
1537 * update fails because of an concurrent update, will return false.
1540 * the device to update
1542 * the device key for the device
1543 * @return true if the update succeeded, false otherwise.
1545 private boolean updateIndices(Device device, Long deviceKey) {
1546 if (!primaryIndex.updateIndex(device, deviceKey)) {
1549 IEntityClass entityClass = device.getEntityClass();
1550 ClassState classState = getClassState(entityClass);
1552 if (classState.classIndex != null) {
1553 if (!classState.classIndex.updateIndex(device, deviceKey))
1560 * Update the secondary indices for the given entity and associated entity
1564 * the entity to update
1565 * @param entityClass
1566 * the entity class for the entity
1568 * the device key to set up
1570 private void updateSecondaryIndices(Entity entity,
1571 IEntityClass entityClass, Long deviceKey) {
1572 for (DeviceIndex index : secondaryIndexMap.values()) {
1573 index.updateIndex(entity, deviceKey);
1575 ClassState state = getClassState(entityClass);
1576 for (DeviceIndex index : state.secondaryIndexMap.values()) {
1577 index.updateIndex(entity, deviceKey);
1582 * Clean up expired entities/devices
1584 protected void cleanupEntities() {
1585 // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
1587 Calendar c = Calendar.getInstance();
1588 c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
1589 Date cutoff = c.getTime();
1591 ArrayList<Entity> toRemove = new ArrayList<Entity>();
1592 ArrayList<Entity> toKeep = new ArrayList<Entity>();
1594 Iterator<Device> diter = deviceMap.values().iterator();
1595 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1597 while (diter.hasNext()) {
1598 Device d = diter.next();
1601 deviceUpdates.clear();
1604 for (Entity e : d.getEntities()) {
1605 if (e.getLastSeenTimestamp() != null
1606 && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
1607 // individual entity needs to be removed
1613 if (toRemove.size() == 0) {
1617 // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
1618 for (Entity e : toRemove) {
1619 removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
1623 if (toKeep.size() > 0) {
1624 Device newDevice = allocateDevice(d.getDeviceKey(),
1625 d.getDHCPClientName(), d.oldAPs,
1626 d.attachmentPoints, toKeep, d.getEntityClass());
1628 EnumSet<DeviceField> changedFields = EnumSet
1629 .noneOf(DeviceField.class);
1630 for (Entity e : toRemove) {
1631 changedFields.addAll(findChangedFields(newDevice, e));
1633 DeviceUpdate update = null;
1634 if (changedFields.size() > 0) {
1635 update = new DeviceUpdate(d, CHANGE, changedFields);
1638 if (!deviceMap.replace(newDevice.getDeviceKey(), d,
1640 // concurrent modification; try again
1641 // need to use device that is the map now for the next
1643 d = deviceMap.get(d.getDeviceKey());
1647 if (update != null) {
1648 // need to count after all possibly continue stmts in
1650 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1651 deviceUpdates.add(update);
1654 DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
1655 if (!deviceMap.remove(d.getDeviceKey(), d)) {
1656 // concurrent modification; try again
1657 // need to use device that is the map now for the next
1659 d = deviceMap.get(d.getDeviceKey());
1662 // debugCounters.updateCounter(CNT_DEVICE_DELETED);
1664 deviceUpdates.add(update);
1666 processUpdates(deviceUpdates);
1672 protected void removeEntity(Entity removed, IEntityClass entityClass,
1673 Long deviceKey, Collection<Entity> others) {
1674 // Don't count in this method. This method CAN BE called to clean-up
1675 // after concurrent device adds/updates and thus counting here
1677 for (DeviceIndex index : secondaryIndexMap.values()) {
1678 index.removeEntityIfNeeded(removed, deviceKey, others);
1680 ClassState classState = getClassState(entityClass);
1681 for (DeviceIndex index : classState.secondaryIndexMap.values()) {
1682 index.removeEntityIfNeeded(removed, deviceKey, others);
1685 primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
1687 if (classState.classIndex != null) {
1688 classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
1694 * method to delete a given device, remove all entities first and then
1695 * finally delete the device itself.
1699 protected void deleteDevice(Device device) {
1700 // Don't count in this method. This method CAN BE called to clean-up
1701 // after concurrent device adds/updates and thus counting here
1703 ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
1704 for (Entity entity : device.getEntities()) {
1705 this.removeEntity(entity, device.getEntityClass(),
1706 device.getDeviceKey(), emptyToKeep);
1708 if (!deviceMap.remove(device.getDeviceKey(), device)) {
1709 if (logger.isDebugEnabled())
1710 logger.debug("device map does not have this device -"
1711 + device.toString());
1715 private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
1716 Integer ipv4Address, NodeConnector port) {
1717 // FIXME: vlan==null is a valid search. Need to handle this
1718 // case correctly. Note that the code will still work correctly.
1719 // But we might do a full device search instead of using an index.
1720 EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
1721 if (macAddress != null)
1722 keys.add(DeviceField.MAC);
1724 keys.add(DeviceField.VLAN);
1725 if (ipv4Address != null)
1726 keys.add(DeviceField.IPV4);
1728 keys.add(DeviceField.SWITCHPORT);
1732 protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
1733 EnumSet<DeviceField> keyFields, Entity entity) {
1734 ClassState classState = getClassState(clazz);
1735 DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
1737 return Collections.<Device> emptySet().iterator();
1738 return new DeviceIndexInterator(this, index.queryByEntity(entity));
1741 protected Device allocateDevice(Long deviceKey, Entity entity,
1742 IEntityClass entityClass) {
1743 return new Device(this, deviceKey, entity, entityClass);
1747 protected Device allocateDevice(Long deviceKey, String dhcpClientName,
1748 List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
1749 Collection<Entity> entities, IEntityClass entityClass) {
1750 return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
1751 entities, entityClass);
1754 protected Device allocateDevice(Device device, Entity entity,
1755 int insertionpoint) {
1756 return new Device(device, entity, insertionpoint);
1760 protected Device allocateDevice(Device device, Set<Entity> entities) {
1761 List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
1762 List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
1763 for (Entity entity : entities) {
1764 if (entity.getPort() != null) {
1765 AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
1766 newPossibleAPs.add(aP);
1769 if (device.attachmentPoints != null) {
1770 for (AttachmentPoint oldAP : device.attachmentPoints) {
1771 if (newPossibleAPs.contains(oldAP)) {
1776 if (newAPs.isEmpty())
1778 Device d = new Device(this, device.getDeviceKey(),
1779 device.getDHCPClientName(), newAPs, null, entities,
1780 device.getEntityClass());
1781 d.updateAttachmentPoint();
1785 // *********************
1786 // ITopologyManagerAware
1787 // *********************
1790 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
1791 Iterator<Device> diter = deviceMap.values().iterator();
1793 while (diter.hasNext()) {
1794 Device d = diter.next();
1795 if (d.updateAttachmentPoint()) {
1796 if (logger.isDebugEnabled()) {
1797 logger.debug("Attachment point changed for device: {}", d);
1799 sendDeviceMovedNotification(d);
1805 public void edgeOverUtilized(Edge edge) {
1810 public void edgeUtilBackToNormal(Edge edge) {
1814 // *********************
1815 // IEntityClassListener
1816 // *********************
1819 public void entityClassChanged(Set<String> entityClassNames) {
1821 * iterate through the devices, reclassify the devices that belong to
1822 * these entity class names
1824 Iterator<Device> diter = deviceMap.values().iterator();
1825 while (diter.hasNext()) {
1826 Device d = diter.next();
1827 if (d.getEntityClass() == null
1828 || entityClassNames.contains(d.getEntityClass().getName()))
1829 reclassifyDevice(d);
1837 * Send update notifications to listeners
1840 * the updates to process.
1842 protected void sendDeviceMovedNotification(Device d) {
1843 // debugCounters.updateCounter(CNT_DEVICE_MOVED);
1844 // deviceSyncManager.storeDevice(d);
1845 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1846 if (listeners != null) {
1847 for (IDeviceListener listener : listeners) {
1848 listener.deviceMoved(d);
1854 * this method will reclassify and reconcile a device - possibilities are -
1855 * create new device(s), remove entities from this device. If the device
1856 * entity class did not change then it returns false else true.
1860 protected boolean reclassifyDevice(Device device) {
1861 // first classify all entities of this device
1862 if (device == null) {
1863 logger.debug("In reclassify for null device");
1866 boolean needToReclassify = false;
1867 for (Entity entity : device.entities) {
1868 IEntityClass entityClass = this.entityClassifier
1869 .classifyEntity(entity);
1870 if (entityClass == null || device.getEntityClass() == null) {
1871 needToReclassify = true;
1874 if (!entityClass.getName()
1875 .equals(device.getEntityClass().getName())) {
1876 needToReclassify = true;
1880 if (needToReclassify == false) {
1884 // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
1885 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1886 // delete this device and then re-learn all the entities
1887 this.deleteDevice(device);
1888 deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
1890 if (!deviceUpdates.isEmpty())
1891 processUpdates(deviceUpdates);
1892 for (Entity entity : device.entities) {
1893 this.learnDeviceByEntity(entity);
1899 * For testing: sets the interval between writes of the same device to the
1904 // void setSyncStoreWriteInterval(int intervalMs) {
1905 // this.syncStoreWriteIntervalMs = intervalMs;
1909 * For testing: sets the time between transition to MASTER and consolidate
1914 // void setInitialSyncStoreConsolidateMs(int intervalMs) {
1915 // this.initialSyncStoreConsolidateMs = intervalMs;
1918 private long toLong(byte[] address) {
1920 for (int i = 0; i < 6; i++) {
1921 long t = (address[i] & 0xffL) << ((5 - i) * 8);
1928 * Accepts an IPv4 address in a byte array and returns the corresponding
1929 * 32-bit integer value.
1934 private static int toIPv4Address(byte[] ipAddress) {
1936 for (int i = 0; i < 4; i++) {
1937 int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
1943 private void registerDeviceManagerDebugCounters() {
1945 * XXX Missing functionality if (debugCounters == null) {
1946 * logger.error("Debug Counter Service not found."); debugCounters = new
1947 * NullDebugCounter(); return; }
1948 * debugCounters.registerCounter(CNT_INCOMING,
1949 * "All incoming packets seen by this module",
1950 * CounterType.ALWAYS_COUNT);
1951 * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
1952 * "Number of flows that have been received for reconciliation by " +
1953 * "this module", CounterType.ALWAYS_COUNT);
1954 * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
1955 * "Number of flow reconcile events that failed because no source " +
1956 * "device could be identified", CounterType.WARN); // is this really a
1957 * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
1958 * "Number of flow reconcile events that failed because no " +
1959 * "destination device could be identified", CounterType.WARN); // is
1960 * this really a warning
1961 * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
1962 * "Number of packetIns that were discarded because the source " +
1963 * "MAC was broadcast or multicast", CounterType.WARN);
1964 * debugCounters.registerCounter(CNT_NO_SOURCE,
1965 * "Number of packetIns that were discarded because the " +
1966 * "could not identify a source device. This can happen if a " +
1967 * "packet is not allowed, appears on an illegal port, does not " +
1968 * "have a valid address space, etc.", CounterType.WARN);
1969 * debugCounters.registerCounter(CNT_NO_DEST,
1970 * "Number of packetIns that did not have an associated " +
1971 * "destination device. E.g., because the destination MAC is " +
1972 * "broadcast/multicast or is not yet known to the controller.",
1973 * CounterType.ALWAYS_COUNT);
1974 * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
1975 * "Number of times a DHCP client name was snooped from a " +
1976 * "packetIn.", CounterType.ALWAYS_COUNT);
1977 * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
1978 * "Number of times packetIn was received on an internal port and" +
1979 * "no source device is known for the source MAC. The packetIn is " +
1980 * "discarded.", CounterType.WARN);
1981 * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
1982 * "Number of times a packetIn was not allowed due to spoofing " +
1983 * "protection configuration.", CounterType.WARN); // is this really a
1984 * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
1985 * "Number of times a new device was learned",
1986 * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
1987 * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
1988 * "Number of times a packetIn was received on an internal port " +
1989 * "for a known device.", CounterType.ALWAYS_COUNT);
1990 * debugCounters.registerCounter(CNT_NEW_ENTITY,
1991 * "Number of times a new entity was learned for an existing device",
1992 * CounterType.ALWAYS_COUNT);
1993 * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
1994 * "Number of times device properties have changed",
1995 * CounterType.ALWAYS_COUNT);
1996 * debugCounters.registerCounter(CNT_DEVICE_MOVED,
1997 * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
1998 * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
1999 * "Number of times the entity cleanup task has been run",
2000 * CounterType.ALWAYS_COUNT);
2001 * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
2002 * "Number of times entities have been removed due to timeout " +
2003 * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
2004 * CounterType.ALWAYS_COUNT);
2005 * debugCounters.registerCounter(CNT_DEVICE_DELETED,
2006 * "Number of devices that have been removed due to inactivity",
2007 * CounterType.ALWAYS_COUNT);
2008 * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
2009 * "Number of devices that required reclassification and have been " +
2010 * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
2011 * debugCounters.registerCounter(CNT_DEVICE_STORED,
2012 * "Number of device entries written or updated to the sync store",
2013 * CounterType.ALWAYS_COUNT);
2014 * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
2015 * "Number of times a device update to the sync store was " +
2016 * "requested but not performed because the same device entities " +
2017 * "have recently been updated already", CounterType.ALWAYS_COUNT);
2018 * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
2019 * "Number of devices that were removed from the sync store " +
2020 * "because the local controller removed the device due to " +
2021 * "inactivity", CounterType.ALWAYS_COUNT);
2022 * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
2023 * "Number of times an operation on the sync store resulted in " +
2024 * "sync exception", CounterType.WARN); // it this an error?
2025 * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
2026 * "Number of devices that were read from the sync store after " +
2027 * "the local controller transitioned from SLAVE to MASTER",
2028 * CounterType.ALWAYS_COUNT);
2029 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
2030 * "Number of times the task to consolidate entries in the " +
2031 * "store witch live known devices has been run",
2032 * CounterType.ALWAYS_COUNT);
2033 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
2034 * "Number of times a device has been removed from the sync " +
2035 * "store because no corresponding live device is known. " +
2036 * "This indicates a remote controller still writing device " +
2037 * "entries despite the local controller being MASTER or an " +
2038 * "incosistent store update from the local controller.",
2039 * CounterType.WARN);
2040 * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
2041 * "Number of times this controller has transitioned from SLAVE " +
2042 * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
2047 * For testing: consolidate the store NOW
2049 // void scheduleConsolidateStoreNow() {
2050 // this.storeConsolidateTask.reschedule(0, TimeUnit.MILLISECONDS);
2053 // private class DeviceSyncManager {
2054 // // maps (opaque) deviceKey to the time in System.nanoTime() when we
2055 // // last wrote the device to the sync store
2056 // private ConcurrentMap<Long, Long> lastWriteTimes =
2057 // new ConcurrentHashMap<Long, Long>();
2060 // * Write the given device to storage if we are MASTER.
2061 // * Use this method if the device has significantly changed (e.g.,
2062 // * new AP, new IP, entities removed).
2063 // * @param d the device to store
2065 // public void storeDevice(Device d) {
2070 // long now = System.nanoTime();
2071 // writeUpdatedDeviceToStorage(d);
2072 // lastWriteTimes.put(d.getDeviceKey(), now);
2076 // * Write the given device to storage if we are MASTER and if the
2077 // * last write for the device was more than this.syncStoreIntervalNs
2079 // * Use this method to updated last active times in the store.
2080 // * @param d the device to store
2082 // public void storeDeviceThrottled(Device d) {
2083 // long intervalNs = syncStoreWriteIntervalMs*1000L*1000L;
2088 // long now = System.nanoTime();
2089 // Long last = lastWriteTimes.get(d.getDeviceKey());
2090 // if (last == null ||
2091 // now - last > intervalNs) {
2092 // writeUpdatedDeviceToStorage(d);
2093 // lastWriteTimes.put(d.getDeviceKey(), now);
2095 // debugCounters.updateCounter(CNT_DEVICE_STORE_THROTTLED);
2100 // * Remove the given device from the store. If only some entities have
2101 // * been removed the updated device should be written using
2102 // * {@link #storeDevice(Device)}
2105 // public void removeDevice(Device d) {
2108 // // FIXME: could we have a problem with concurrent put to the
2109 // // hashMap? I.e., we write a stale entry to the map after the
2110 // // delete and now are left with an entry we'll never clean up
2111 // lastWriteTimes.remove(d.getDeviceKey());
2113 // // TODO: should probably do versioned delete. OTOH, even
2114 // // if we accidentally delete, we'll write it again after
2115 // // the next entity ....
2116 // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2117 // storeClient.delete(DeviceSyncRepresentation.computeKey(d));
2118 // } catch(ObsoleteVersionException e) {
2120 // } catch (SyncException e) {
2121 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2122 // logger.error("Could not remove device " + d + " from store", e);
2127 // * Remove the given Versioned device from the store. If the device
2128 // * was locally modified ignore the delete request.
2129 // * @param syncedDeviceKey
2131 // private void removeDevice(Versioned<DeviceSyncRepresentation> dev) {
2133 // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2134 // storeClient.delete(dev.getValue().getKey(),
2135 // dev.getVersion());
2136 // } catch(ObsoleteVersionException e) {
2137 // // Key was locally modified by another thread.
2138 // // Do not delete and ignore.
2139 // } catch(SyncException e) {
2140 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2141 // logger.error("Failed to remove device entry for " +
2142 // dev.toString() + " from store.", e);
2147 // * Synchronously transition from SLAVE to MASTER. By iterating through
2148 // * the store and learning all devices from the store
2150 // private void goToMaster() {
2151 // if (logger.isDebugEnabled()) {
2152 // logger.debug("Transitioning to MASTER role");
2154 // debugCounters.updateCounter(CNT_TRANSITION_TO_MASTER);
2155 // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2158 // iter = storeClient.entries();
2159 // } catch (SyncException e) {
2160 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2161 // logger.error("Failed to read devices from sync store", e);
2165 // while(iter.hasNext()) {
2166 // Versioned<DeviceSyncRepresentation> versionedDevice =
2167 // iter.next().getValue();
2168 // DeviceSyncRepresentation storedDevice =
2169 // versionedDevice.getValue();
2170 // if (storedDevice == null)
2172 // debugCounters.updateCounter(CNT_DEVICES_FROM_STORE);
2173 // for(SyncEntity se: storedDevice.getEntities()) {
2174 // learnDeviceByEntity(se.asEntity());
2178 // if (iter != null)
2181 // storeConsolidateTask.reschedule(initialSyncStoreConsolidateMs,
2182 // TimeUnit.MILLISECONDS);
2186 // * Actually perform the write of the device to the store
2187 // * FIXME: concurrent modification behavior
2188 // * @param device The device to write
2190 // private void writeUpdatedDeviceToStorage(Device device) {
2192 // debugCounters.updateCounter(CNT_DEVICE_STORED);
2193 // // FIXME: use a versioned put
2194 // DeviceSyncRepresentation storeDevice =
2195 // new DeviceSyncRepresentation(device);
2196 // storeClient.put(storeDevice.getKey(), storeDevice);
2197 // } catch (ObsoleteVersionException e) {
2198 // // FIXME: what's the right behavior here. Can the store client
2199 // // even throw this error?
2200 // } catch (SyncException e) {
2201 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2202 // logger.error("Could not write device " + device +
2203 // " to sync store:", e);
2208 // * Iterate through all entries in the sync store. For each device
2209 // * in the store check if any stored entity matches a live device. If
2210 // * no entities match a live device we remove the entry from the store.
2212 // * Note: we do not check if all devices known to device manager are
2213 // * in the store. We rely on regular packetIns for that.
2214 // * Note: it's possible that multiple entries in the store map to the
2215 // * same device. We don't check or handle this case.
2217 // * We need to perform this check after a SLAVE->MASTER transition to
2218 // * get rid of all entries the old master might have written to the
2219 // * store after we took over. We also run it regularly in MASTER
2220 // * state to ensure we don't have stale entries in the store
2222 // private void consolidateStore() {
2225 // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_RUNS);
2226 // if (logger.isDebugEnabled()) {
2227 // logger.debug("Running consolidateStore.");
2229 // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2232 // iter = storeClient.entries();
2233 // } catch (SyncException e) {
2234 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2235 // logger.error("Failed to read devices from sync store", e);
2239 // while(iter.hasNext()) {
2240 // boolean found = false;
2241 // Versioned<DeviceSyncRepresentation> versionedDevice =
2242 // iter.next().getValue();
2243 // DeviceSyncRepresentation storedDevice =
2244 // versionedDevice.getValue();
2245 // if (storedDevice == null)
2247 // for(SyncEntity se: storedDevice.getEntities()) {
2249 // // Do we have a device for this entity??
2250 // IDevice d = findDevice(se.macAddress, se.vlan,
2258 // } catch (IllegalArgumentException e) {
2259 // // not all key fields provided. Skip entity
2263 // // We currently DO NOT have a live device that
2264 // // matches the current device from the store.
2265 // // Delete device from store.
2266 // if (logger.isDebugEnabled()) {
2267 // logger.debug("Removing device {} from store. No "
2268 // + "corresponding live device",
2269 // storedDevice.getKey());
2271 // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED);
2272 // removeDevice(versionedDevice);
2276 // if (iter != null)
2284 // * For testing. Sets the syncService. Only call after init but before
2285 // * startUp. Used by MockDeviceManager
2286 // * @param syncService
2288 // protected void setSyncServiceIfNotSet(ISyncService syncService) {
2289 // if (this.syncService == null)
2290 // this.syncService = syncService;