Add curly braces to if/else statements. (sonar)
[controller.git] / opendaylight / hosttracker_new / implementation / src / main / java / org / opendaylight / controller / hosttracker / internal / DeviceManagerImpl.java
1 /*
2  * Copyright (c) 2011,2012 Big Switch Networks, Inc.
3  *
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
7  *
8  *      http://www.eclipse.org/legal/epl-v10.html
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
13  * implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  *
16  * This file incorporates work covered by the following copyright and
17  * permission notice:
18  *
19  *    Originally created by David Erickson, Stanford University
20  *
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
24  *
25  *         http://www.apache.org/licenses/LICENSE-2.0
26  *
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.
32  */
33
34 package org.opendaylight.controller.hosttracker.internal;
35
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;
39
40 import java.net.InetAddress;
41 import java.net.UnknownHostException;
42 import java.util.ArrayList;
43 import java.util.Calendar;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.Date;
48 import java.util.EnumSet;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.LinkedList;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Map.Entry;
56 import java.util.Queue;
57 import java.util.Set;
58 import java.util.concurrent.ConcurrentHashMap;
59 import java.util.concurrent.Executors;
60 import java.util.concurrent.Future;
61 import java.util.concurrent.ScheduledExecutorService;
62 import java.util.concurrent.TimeUnit;
63
64 import org.opendaylight.controller.hosttracker.Entity;
65 import org.opendaylight.controller.hosttracker.IDevice;
66 import org.opendaylight.controller.hosttracker.IDeviceListener;
67 import org.opendaylight.controller.hosttracker.IDeviceService;
68 import org.opendaylight.controller.hosttracker.IEntityClass;
69 import org.opendaylight.controller.hosttracker.IEntityClassListener;
70 import org.opendaylight.controller.hosttracker.IEntityClassifierService;
71 import org.opendaylight.controller.hosttracker.IfIptoHost;
72 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
73 import org.opendaylight.controller.hosttracker.SwitchPort;
74 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
75 import org.opendaylight.controller.sal.core.ConstructionException;
76 import org.opendaylight.controller.sal.core.Edge;
77 import org.opendaylight.controller.sal.core.Host;
78 import org.opendaylight.controller.sal.core.Node;
79 import org.opendaylight.controller.sal.core.NodeConnector;
80 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
81 import org.opendaylight.controller.sal.core.Property;
82 import org.opendaylight.controller.sal.core.State;
83 import org.opendaylight.controller.sal.core.Tier;
84 import org.opendaylight.controller.sal.core.UpdateType;
85 import org.opendaylight.controller.sal.packet.ARP;
86 import org.opendaylight.controller.sal.packet.Ethernet;
87 import org.opendaylight.controller.sal.packet.IDataPacketService;
88 import org.opendaylight.controller.sal.packet.IListenDataPacket;
89 import org.opendaylight.controller.sal.packet.Packet;
90 import org.opendaylight.controller.sal.packet.PacketResult;
91 import org.opendaylight.controller.sal.packet.RawPacket;
92 import org.opendaylight.controller.sal.packet.address.DataLinkAddress;
93 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
94 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
95 import org.opendaylight.controller.sal.utils.HexEncode;
96 import org.opendaylight.controller.sal.utils.ListenerDispatcher;
97 import org.opendaylight.controller.sal.utils.MultiIterator;
98 import org.opendaylight.controller.sal.utils.NetUtils;
99 import org.opendaylight.controller.sal.utils.SingletonTask;
100 import org.opendaylight.controller.sal.utils.Status;
101 import org.opendaylight.controller.sal.utils.StatusCode;
102 import org.opendaylight.controller.switchmanager.IInventoryListener;
103 import org.opendaylight.controller.switchmanager.ISwitchManager;
104 import org.opendaylight.controller.topologymanager.ITopologyManager;
105 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
106 import org.slf4j.Logger;
107 import org.slf4j.LoggerFactory;
108
109 /**
110  * DeviceManager creates Devices based upon MAC addresses seen in the network.
111  * It tracks any network addresses mapped to the Device, and its location within
112  * the network.
113  *
114  * @author readams
115  */
116 public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
117         IListenDataPacket, ITopologyManagerAware, IfIptoHost,
118         IInventoryListener {
119     protected static Logger logger = LoggerFactory
120             .getLogger(DeviceManagerImpl.class);
121
122     public static final String MODULE_NAME = "devicemanager";
123
124     // protected ITopologyService topology;
125     // protected IStorageSourceService storageSource;
126     // protected IRestApiService restApi;
127     // protected IThreadPoolService threadPool;
128     // protected IFlowReconcileService flowReconcileMgr;
129     // protected IFlowReconcileEngineService flowReconcileEngine;
130     // protected IDebugCounterService debugCounters;
131     // private ISyncService syncService;
132     // private IStoreClient<String,DeviceSyncRepresentation> storeClient;
133     // private DeviceSyncManager deviceSyncManager;
134
135     private ITopologyManager topology;
136     private ISwitchManager switchManager = null;
137     private IDataPacketService dataPacketService = null;
138
139     public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
140     public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
141             + "-reconcileRequest";
142     public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
143             + "-reconcileNoSourceDevice";
144     public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
145             + "-reconcileNoDestDevice";
146     public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
147             + "-broadcastSource";
148     public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
149     public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
150     public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
151             + "-dhcpClientNameSnooped";
152     public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
153             + "-deviceOnInternalPortNotLearned";
154     public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
155             + "-packetNotAllowed";
156     public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
157     public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
158             + "-packetOnInternalPortForKnownDevice";
159     public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
160     public static final String CNT_DEVICE_CHANGED = MODULE_NAME
161             + "-deviceChanged";
162     public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
163     public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
164             + "-cleanupEntitiesRuns";
165     public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
166             + "-entityRemovedTimeout";
167     public static final String CNT_DEVICE_DELETED = MODULE_NAME
168             + "-deviceDeleted";
169     public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
170             + "-deviceReclassifyDelete";
171     public static final String CNT_DEVICE_STORED = MODULE_NAME
172             + "-deviceStored";
173     public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
174             + "-deviceStoreThrottled";
175     public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
176             + "-deviceRemovedFromStore";
177     public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
178             + "-syncException";
179     public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
180             + "-devicesFromStore";
181     public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
182             + "-consolidateStoreRuns";
183     public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
184             + "-consolidateStoreDevicesRemoved";
185
186     static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
187             .getCanonicalName() + ".stateStore";
188
189     /**
190      * Time interval between writes of entries for the same device to the sync
191      * store.
192      */
193     // static final int DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS =
194     // 5*60*1000; // 5 min
195     // private int syncStoreWriteIntervalMs =
196     // DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS;
197
198     /**
199      * Time after SLAVE->MASTER until we run the consolidate store code.
200      */
201     // static final int DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS =
202     // 15*1000; // 15 sec
203     // private int initialSyncStoreConsolidateMs =
204     // DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS;
205
206     /**
207      * Time interval between consolidate store runs.
208      */
209     // static final int DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS =
210     // 75*60*1000; // 75 min
211     // private final int syncStoreConsolidateIntervalMs =
212     // DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS;
213
214     /**
215      * Time in milliseconds before entities will expire
216      */
217     protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
218
219     /**
220      * Time in seconds between cleaning up old entities/devices
221      */
222     protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
223
224     /**
225      * This is the master device map that maps device IDs to {@link Device}
226      * objects.
227      */
228     protected ConcurrentHashMap<Long, Device> deviceMap;
229
230     protected ConcurrentHashMap<NodeConnector, Entity> inactiveStaticDevices;
231     /**
232      * Counter used to generate device keys
233      */
234     protected long deviceKeyCounter = 0;
235
236     /**
237      * Lock for incrementing the device key counter
238      */
239     protected Object deviceKeyLock = new Object();
240
241     /**
242      * This is the primary entity index that contains all entities
243      */
244     protected DeviceUniqueIndex primaryIndex;
245
246     /**
247      * This stores secondary indices over the fields in the devices
248      */
249     protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
250
251     /**
252      * This map contains state for each of the {@ref IEntityClass} that exist
253      */
254     protected ConcurrentHashMap<String, ClassState> classStateMap;
255
256     /**
257      * This is the list of indices we want on a per-class basis
258      */
259     protected Set<EnumSet<DeviceField>> perClassIndices;
260
261     /**
262      * The entity classifier currently in use
263      */
264     protected IEntityClassifierService entityClassifier;
265
266     /**
267      * Used to cache state about specific entity classes
268      */
269     protected class ClassState {
270
271         /**
272          * The class index
273          */
274         protected DeviceUniqueIndex classIndex;
275
276         /**
277          * This stores secondary indices over the fields in the device for the
278          * class
279          */
280         protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
281
282         /**
283          * Allocate a new {@link ClassState} object for the class
284          *
285          * @param clazz
286          *            the class to use for the state
287          */
288         public ClassState(IEntityClass clazz) {
289             EnumSet<DeviceField> keyFields = clazz.getKeyFields();
290             EnumSet<DeviceField> primaryKeyFields = entityClassifier
291                     .getKeyFields();
292             boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
293
294             if (!keyFieldsMatchPrimary)
295                 classIndex = new DeviceUniqueIndex(keyFields);
296
297             secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
298             for (EnumSet<DeviceField> fields : perClassIndices) {
299                 secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
300             }
301         }
302     }
303
304     /**
305      * Device manager event listeners reclassifyDeviceListeners are notified
306      * first before reconcileDeviceListeners. This is to make sure devices are
307      * correctly reclassified before reconciliation.
308      */
309     protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
310
311     /**
312      * Using the IfNewHostNotify to notify listeners of host changes.
313      */
314     private Set<IfNewHostNotify> newHostNotify = Collections
315             .synchronizedSet(new HashSet<IfNewHostNotify>());
316
317     /**
318      * A device update event to be dispatched
319      */
320     protected static class DeviceUpdate {
321         public enum Change {
322             ADD, DELETE, CHANGE;
323         }
324
325         /**
326          * The affected device
327          */
328         protected Device device;
329
330         /**
331          * The change that was made
332          */
333         protected Change change;
334
335         /**
336          * If not added, then this is the list of fields changed
337          */
338         protected EnumSet<DeviceField> fieldsChanged;
339
340         public DeviceUpdate(Device device, Change change,
341                 EnumSet<DeviceField> fieldsChanged) {
342             super();
343             this.device = device;
344             this.change = change;
345             this.fieldsChanged = fieldsChanged;
346         }
347
348         @Override
349         public String toString() {
350             String devIdStr = device.getEntityClass().getName() + "::"
351                     + device.getMACAddressString();
352             return "DeviceUpdate [device=" + devIdStr + ", change=" + change
353                     + ", fieldsChanged=" + fieldsChanged + "]";
354         }
355
356     }
357
358     /**
359      * AttachmentPointComparator
360      *
361      * Compares two attachment points and returns the latest one. It is assumed
362      * that the two attachment points are in the same L2 domain.
363      *
364      * @author srini
365      */
366     protected class AttachmentPointComparator implements
367             Comparator<AttachmentPoint> {
368         public AttachmentPointComparator() {
369             super();
370         }
371
372         @Override
373         public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
374             // First compare based on L2 domain ID;
375
376             // XXX - missing functionality -- need topology
377             // long oldDomain = topology.getL2DomainId(oldSw);
378             // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
379             long oldDomain = 0;
380             boolean oldBD = false;
381
382             // XXX - missing functionality -- need topology
383             // long newDomain = topology.getL2DomainId(newSw);
384             // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
385             long newDomain = 0;
386             boolean newBD = false;
387
388             if (oldDomain < newDomain) {
389                return -1;
390             } else if (oldDomain > newDomain) {
391                 return 1;
392             }
393             // Give preference to OFPP_LOCAL always
394             if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
395                     && newAP.getPort().getType()
396                             .equals(NodeConnectorIDType.SWSTACK)) {
397                 return -1;
398             } else if (oldAP.getPort().getType()
399                     .equals(NodeConnectorIDType.SWSTACK)
400                     && !newAP.getPort().getType()
401                             .equals(NodeConnectorIDType.SWSTACK)) {
402                 return 1;
403             }
404
405             // We expect that the last seen of the new AP is higher than
406             // old AP, if it is not, just reverse and send the negative
407             // of the result.
408             if (oldAP.getActiveSince() > newAP.getActiveSince())
409                 return -compare(newAP, oldAP);
410
411             long activeOffset = 0;
412             // XXX - missing functionality -- need topology
413             // if (!topology.isConsistent(oldSw, oldPort, newSw, newPort)) {
414             if (!newBD && oldBD) {
415                 return -1;
416             }
417             if (newBD && oldBD) {
418                 activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
419             } else if (newBD && !oldBD) {
420                 activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
421             }
422
423             // } else {
424             // // The attachment point is consistent.
425             // activeOffset = AttachmentPoint.CONSISTENT_TIMEOUT;
426             // }
427
428             if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
429                     || (newAP.getLastSeen() > oldAP.getLastSeen()
430                             + AttachmentPoint.INACTIVITY_INTERVAL)) {
431                 return -1;
432             }
433             return 1;
434         }
435     }
436
437     /**
438      * Comparator for sorting by cluster ID
439      */
440     public AttachmentPointComparator apComparator;
441
442     /**
443      * Switch ports where attachment points shouldn't be learned
444      */
445     private Set<SwitchPort> suppressAPs;
446
447     /**
448      * Periodic task to clean up expired entities
449      */
450     public SingletonTask entityCleanupTask;
451
452     // ********************
453     // Dependency injection
454     // ********************
455
456     void setNewHostNotify(IfNewHostNotify obj) {
457         this.newHostNotify.add(obj);
458     }
459
460     void unsetNewHostNotify(IfNewHostNotify obj) {
461         this.newHostNotify.remove(obj);
462     }
463
464     void setDataPacketService(IDataPacketService s) {
465         this.dataPacketService = s;
466     }
467
468     void unsetDataPacketService(IDataPacketService s) {
469         if (this.dataPacketService == s) {
470             this.dataPacketService = null;
471         }
472     }
473
474     public void setTopologyManager(ITopologyManager s) {
475         this.topology = s;
476     }
477
478     public void unsetTopologyManager(ITopologyManager s) {
479         if (this.topology == s) {
480             logger.debug("Topology Manager Service removed!");
481             this.topology = null;
482         }
483     }
484
485     private volatile boolean stopped = true;
486     private ScheduledExecutorService ses;
487
488     public void stop() {
489         stopped = true;
490         if (ses != null)
491             ses.shutdownNow();
492     }
493
494     public void start() {
495         this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
496
497         // XXX - TODO need to make it possible to register a non-default
498         // classifier
499         entityClassifier = new DefaultEntityClassifier();
500         this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
501         this.suppressAPs = Collections
502                 .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
503         primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
504         secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
505
506         deviceMap = new ConcurrentHashMap<Long, Device>();
507         inactiveStaticDevices = new ConcurrentHashMap<NodeConnector, Entity>();
508         classStateMap = new ConcurrentHashMap<String, ClassState>();
509         apComparator = new AttachmentPointComparator();
510
511         addIndex(true, EnumSet.of(DeviceField.IPV4));
512
513         // floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
514         // floodlightProvider.addHAListener(this.haListenerDelegate);
515         // if (topology != null)
516         // topology.addListener(this);
517         // flowReconcileMgr.addFlowReconcileListener(this);
518         // entityClassifier.addListener(this);
519
520         stopped = false;
521         // XXX - Should use a common threadpool but this doesn't currently exist
522         ses = Executors.newScheduledThreadPool(1);
523         Runnable ecr = new Runnable() {
524             @Override
525             public void run() {
526                 cleanupEntities();
527                 if (!stopped)
528                     entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
529                             TimeUnit.SECONDS);
530             }
531         };
532         entityCleanupTask = new SingletonTask(ses, ecr);
533         entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
534
535         /*
536          * XXX Missing functionality if (restApi != null) {
537          * restApi.addRestletRoutable(new DeviceRoutable()); } else {
538          * logger.debug("Could not instantiate REST API"); }
539          */
540
541         registerDeviceManagerDebugCounters();
542
543         /*
544          * XXX Missing functionality try {
545          * this.syncService.registerStore(DEVICE_SYNC_STORE_NAME, Scope.LOCAL);
546          * this.storeClient = this.syncService
547          * .getStoreClient(DEVICE_SYNC_STORE_NAME, String.class,
548          * DeviceSyncRepresentation.class); } catch (SyncException e) { throw
549          * new FloodlightModuleException("Error while setting up sync service",
550          * e); }
551          *
552          * Runnable consolidateStoreRunner = new Runnable() {
553          *
554          * @Override public void run() { deviceSyncManager.consolidateStore();
555          * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
556          * TimeUnit.MILLISECONDS); debugCounters.flushCounters(); } };
557          * storeConsolidateTask = new SingletonTask(ses,
558          * consolidateStoreRunner); if (isMaster)
559          * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
560          * TimeUnit.MILLISECONDS);
561          */
562     }
563
564     /**
565      * Periodic task to consolidate entries in the store. I.e., delete entries
566      * in the store that are not known to DeviceManager
567      */
568     // XXX - Missing functionality
569     // private SingletonTask storeConsolidateTask;
570
571     // *********************
572     // IDeviceManagerService
573     // *********************
574
575     void setSwitchManager(ISwitchManager s) {
576         logger.debug("SwitchManager set");
577         this.switchManager = s;
578     }
579
580     void unsetSwitchManager(ISwitchManager s) {
581         if (this.switchManager == s) {
582             logger.debug("SwitchManager removed!");
583             this.switchManager = null;
584         }
585     }
586
587     @Override
588     public IDevice getDevice(Long deviceKey) {
589         return deviceMap.get(deviceKey);
590     }
591
592     @Override
593     public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
594             NodeConnector port) throws IllegalArgumentException {
595         if (vlan != null && vlan.shortValue() <= 0)
596             vlan = null;
597         if (ipv4Address != null && ipv4Address == 0)
598             ipv4Address = null;
599         Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
600         if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
601             throw new IllegalArgumentException("Not all key fields specified."
602                     + " Required fields: " + entityClassifier.getKeyFields());
603         }
604         return findDeviceByEntity(e);
605     }
606
607     @Override
608     public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
609             Short vlan, Integer ipv4Address) throws IllegalArgumentException {
610         if (vlan != null && vlan.shortValue() <= 0)
611             vlan = null;
612         if (ipv4Address != null && ipv4Address == 0)
613             ipv4Address = null;
614         Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
615         if (entityClass == null
616                 || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
617             throw new IllegalArgumentException("Not all key fields and/or "
618                     + " no source device specified. Required fields: "
619                     + entityClassifier.getKeyFields());
620         }
621         return findDestByEntity(entityClass, e);
622     }
623
624     @Override
625     public Collection<? extends IDevice> getAllDevices() {
626         return Collections.unmodifiableCollection(deviceMap.values());
627     }
628
629     @Override
630     public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
631         if (perClass) {
632             perClassIndices.add(keyFields);
633         } else {
634             secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
635         }
636     }
637
638     @Override
639     public Iterator<? extends IDevice> queryDevices(Long macAddress,
640             Short vlan, Integer ipv4Address, NodeConnector port) {
641         DeviceIndex index = null;
642         if (secondaryIndexMap.size() > 0) {
643             EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
644                     ipv4Address, port);
645             index = secondaryIndexMap.get(keys);
646         }
647
648         Iterator<Device> deviceIterator = null;
649         if (index == null) {
650             // Do a full table scan
651             deviceIterator = deviceMap.values().iterator();
652         } else {
653             // index lookup
654             Entity entity = new Entity((macAddress == null ? 0 : macAddress),
655                     vlan, ipv4Address, port, null);
656             deviceIterator = new DeviceIndexInterator(this,
657                     index.queryByEntity(entity));
658         }
659
660         DeviceIterator di = new DeviceIterator(deviceIterator, null,
661                 macAddress, vlan, ipv4Address, port);
662         return di;
663     }
664
665     @Override
666     public Iterator<? extends IDevice> queryClassDevices(
667             IEntityClass entityClass, Long macAddress, Short vlan,
668             Integer ipv4Address, NodeConnector port) {
669         ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
670         ClassState classState = getClassState(entityClass);
671
672         DeviceIndex index = null;
673         if (classState.secondaryIndexMap.size() > 0) {
674             EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
675                     ipv4Address, port);
676             index = classState.secondaryIndexMap.get(keys);
677         }
678
679         Iterator<Device> iter;
680         if (index == null) {
681             index = classState.classIndex;
682             if (index == null) {
683                 // scan all devices
684                 return new DeviceIterator(deviceMap.values().iterator(),
685                         new IEntityClass[] { entityClass }, macAddress, vlan,
686                         ipv4Address, port);
687             } else {
688                 // scan the entire class
689                 iter = new DeviceIndexInterator(this, index.getAll());
690             }
691         } else {
692             // index lookup
693             Entity entity = new Entity((macAddress == null ? 0 : macAddress),
694                     vlan, ipv4Address, port, null);
695             iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
696         }
697         iterators.add(iter);
698
699         return new MultiIterator<Device>(iterators.iterator());
700     }
701
702     protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
703             Short vlan, Integer ipv4Address, NodeConnector port) {
704         DeviceIndex index = null;
705         if (secondaryIndexMap.size() > 0) {
706             EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
707                     ipv4Address, port);
708             index = secondaryIndexMap.get(keys);
709         }
710
711         Iterator<Device> deviceIterator = null;
712         if (index == null) {
713             // Do a full table scan
714             deviceIterator = deviceMap.values().iterator();
715         } else {
716             // index lookup
717             Entity entity = new Entity((macAddress == null ? 0 : macAddress),
718                     vlan, ipv4Address, port, null);
719             deviceIterator = new DeviceIndexInterator(this,
720                     index.queryByEntity(entity));
721         }
722
723         DeviceIterator di = new DeviceIterator(deviceIterator, null,
724                 macAddress, vlan, ipv4Address, port);
725         return di;
726     }
727
728     @Override
729     public void addListener(IDeviceListener listener) {
730         deviceListeners.addListener("device", listener);
731         logListeners();
732     }
733
734     @Override
735     public void addSuppressAPs(NodeConnector port) {
736         suppressAPs.add(new SwitchPort(port));
737     }
738
739     @Override
740     public void removeSuppressAPs(NodeConnector port) {
741         suppressAPs.remove(new SwitchPort(port));
742     }
743
744     @Override
745     public Set<SwitchPort> getSuppressAPs() {
746         return Collections.unmodifiableSet(suppressAPs);
747     }
748
749     private void logListeners() {
750         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
751         if (listeners != null) {
752             StringBuffer sb = new StringBuffer();
753             sb.append("DeviceListeners: ");
754             for (IDeviceListener l : listeners) {
755                 sb.append(l.getName());
756                 sb.append(",");
757             }
758             logger.debug(sb.toString());
759         }
760     }
761
762     // ***************
763     // IFlowReconcileListener
764     // ***************
765     /*
766      * XXX - Missing functionality
767      *
768      * @Override public Command reconcileFlows(ArrayList<OFMatchReconcile>
769      * ofmRcList) { ListIterator<OFMatchReconcile> iter =
770      * ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm =
771      * iter.next();
772      *
773      * // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) {
774      * iter.remove(); } }
775      *
776      * if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { return
777      * Command.STOP; } }
778      *
779      * protected Command reconcileFlow(OFMatchReconcile ofm) {
780      * debugCounters.updateCounter(CNT_RECONCILE_REQUEST); // Extract source
781      * entity information Entity srcEntity =
782      * getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) {
783      * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
784      * Command.STOP; }
785      *
786      * // Find the device by source entity Device srcDevice =
787      * findDeviceByEntity(srcEntity); if (srcDevice == null) {
788      * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
789      * Command.STOP; } // Store the source device in the context
790      * fcStore.put(ofm.cntx, CONTEXT_SRC_DEVICE, srcDevice);
791      *
792      * // Find the device matching the destination from the entity // classes of
793      * the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid,
794      * false); Device dstDevice = null; if (dstEntity != null) { dstDevice =
795      * findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice !=
796      * null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else
797      * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } else {
798      * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } if
799      * (logger.isTraceEnabled()) {
800      * logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " +
801      * "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
802      * srcEntity, srcDevice, dstEntity, dstDevice } ); } return
803      * Command.CONTINUE; }
804      */
805
806     // *****************
807     // IListenDataPacket
808     // *****************
809
810     @Override
811     public PacketResult receiveDataPacket(RawPacket inPkt) {
812         // XXX - Can this really pass in null? Why would you ever want that?
813         if (inPkt == null) {
814             return PacketResult.IGNORED;
815         }
816         // try {
817         // throw new Exception("Sample");
818         // } catch (Exception e) {
819         // logger.error("Sample stack trace", e);
820         // }
821
822         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
823         Ethernet eth;
824         if (formattedPak instanceof Ethernet) {
825             eth = (Ethernet) formattedPak;
826         } else {
827             return PacketResult.IGNORED;
828         }
829
830         // Extract source entity information
831         NodeConnector inPort = inPkt.getIncomingNodeConnector();
832         Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
833         if (srcEntity == null) {
834             // debugCounters.updateCounter(CNT_BROADCAST_SOURCE);
835             return PacketResult.CONSUME;
836         }
837
838         // Learn from ARP packet for special VRRP settings.
839         // In VRRP settings, the source MAC address and sender MAC
840         // addresses can be different. In such cases, we need to learn
841         // the IP to MAC mapping of the VRRP IP address. The source
842         // entity will not have that information. Hence, a separate call
843         // to learn devices in such cases.
844         learnDeviceFromArpResponseData(eth, inPort);
845
846         // Learn/lookup device information
847         Device srcDevice = learnDeviceByEntity(srcEntity);
848         if (srcDevice == null) {
849             // debugCounters.updateCounter(CNT_NO_SOURCE);
850             return PacketResult.CONSUME;
851         }
852         logger.trace("Saw packet from device {}", srcDevice);
853
854         // // Store the source device in the context
855         // fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice);
856         //
857         // // Find the device matching the destination from the entity
858         // // classes of the source.
859         // Entity dstEntity = getDestEntityFromPacket(eth);
860         // Device dstDevice = null;
861         // if (dstEntity != null) {
862         // dstDevice =
863         // findDestByEntity(srcDevice.getEntityClass(), dstEntity);
864         // if (dstDevice != null)
865         // fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice);
866         // //else
867         // //debugCounters.updateCounter(CNT_NO_DEST);
868         // } else {
869         // //debugCounters.updateCounter(CNT_NO_DEST);
870         // }
871         //
872         // if (logger.isTraceEnabled()) {
873         // logger.trace("Received PI: {} on switch {}, port {} *** eth={}" +
874         // " *** srcDev={} *** dstDev={} *** ",
875         // new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
876         // srcDevice, dstDevice });
877         // }
878         //
879         // snoopDHCPClientName(eth, srcDevice);
880
881         return PacketResult.KEEP_PROCESSING;
882     }
883
884     // ****************
885     // Internal methods
886     // ****************
887
888     /**
889      * Snoop and record client-provided host name from DHCP requests
890      *
891      * @param eth
892      * @param srcDevice
893      */
894     // private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
895     // if (! (eth.getPayload() instanceof IPv4) )
896     // return;
897     // IPv4 ipv4 = (IPv4) eth.getPayload();
898     // if (! (ipv4.getPayload() instanceof UDP) )
899     // return;
900     // UDP udp = (UDP) ipv4.getPayload();
901     // if (!(udp.getPayload() instanceof DHCP))
902     // return;
903     // DHCP dhcp = (DHCP) udp.getPayload();
904     // byte opcode = dhcp.getOpCode();
905     // if (opcode == DHCP.OPCODE_REQUEST) {
906     // DHCPOption dhcpOption = dhcp.getOption(
907     // DHCPOptionCode.OptionCode_Hostname);
908     // if (dhcpOption != null) {
909     // debugCounters.updateCounter(CNT_DHCP_CLIENT_NAME_SNOOPED);
910     // srcDevice.dhcpClientName = new String(dhcpOption.getData());
911     // }
912     // }
913     // }
914
915     /**
916      * Check whether the given attachment point is valid given the current
917      * topology
918      *
919      * @param switchDPID
920      *            the DPID
921      * @param switchPort
922      *            the port
923      * @return true if it's a valid attachment point
924      */
925     public boolean isValidAttachmentPoint(NodeConnector port) {
926         // XXX - missing functionality -- need topology module
927         // if (topology.isAttachmentPointPort(port) == false)
928         // return false;
929         if (topology.isInternal(port))
930             return false;
931         if (!switchManager.isNodeConnectorEnabled(port))
932             return false;
933         if (suppressAPs.contains(new SwitchPort(port)))
934             return false;
935
936         return true;
937     }
938
939     /**
940      * Get sender IP address from packet if the packet is either an ARP packet.
941      *
942      * @param eth
943      * @param dlAddr
944      * @return
945      */
946     private int getSrcNwAddr(Ethernet eth, long dlAddr) {
947         if (eth.getPayload() instanceof ARP) {
948             ARP arp = (ARP) eth.getPayload();
949             if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
950                     && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
951                 return toIPv4Address(arp.getSenderProtocolAddress());
952             }
953         }
954         return 0;
955     }
956
957     /**
958      * Parse an entity from an {@link Ethernet} packet.
959      *
960      * @param eth
961      *            the packet to parse
962      * @param sw
963      *            the switch on which the packet arrived
964      * @param pi
965      *            the original packetin
966      * @return the entity from the packet
967      */
968     protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
969         byte[] dlAddrArr = eth.getSourceMACAddress();
970         long dlAddr = toLong(dlAddrArr);
971
972         // Ignore broadcast/multicast source
973         if ((dlAddrArr[0] & 0x1) != 0)
974             return null;
975
976         // XXX missing functionality
977         // short vlan = 0;
978         int nwSrc = getSrcNwAddr(eth, dlAddr);
979         return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
980                 new Date());
981     }
982
983     /**
984      * Learn device from ARP data in scenarios where the Ethernet source MAC is
985      * different from the sender hardware address in ARP data.
986      */
987     protected void learnDeviceFromArpResponseData(Ethernet eth,
988             NodeConnector port) {
989
990         if (!(eth.getPayload() instanceof ARP))
991             return;
992         ARP arp = (ARP) eth.getPayload();
993
994         byte[] dlAddrArr = eth.getSourceMACAddress();
995         long dlAddr = toLong(dlAddrArr);
996
997         byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
998         long senderAddr = toLong(senderHardwareAddr);
999
1000         if (dlAddr == senderAddr)
1001             return;
1002
1003         // Ignore broadcast/multicast source
1004         if ((senderHardwareAddr[0] & 0x1) != 0)
1005             return;
1006
1007         // short vlan = eth.getVlanID();
1008         int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
1009
1010         Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
1011                 port, new Date());
1012
1013         learnDeviceByEntity(e);
1014     }
1015
1016     /**
1017      * Get a (partial) entity for the destination from the packet.
1018      *
1019      * @param eth
1020      * @return
1021      */
1022     // protected Entity getDestEntityFromPacket(Ethernet eth) {
1023     // byte[] dlAddrArr = eth.getDestinationMACAddress();
1024     // long dlAddr = Ethernet.toLong(dlAddrArr);
1025     // short vlan = eth.getVlanID();
1026     // int nwDst = 0;
1027     //
1028     // // Ignore broadcast/multicast destination
1029     // if ((dlAddrArr[0] & 0x1) != 0)
1030     // return null;
1031     //
1032     // if (eth.getPayload() instanceof IPv4) {
1033     // IPv4 ipv4 = (IPv4) eth.getPayload();
1034     // nwDst = ipv4.getDestinationAddress();
1035     // }
1036     //
1037     // return new Entity(dlAddr,
1038     // ((vlan >= 0) ? vlan : null),
1039     // ((nwDst != 0) ? nwDst : null),
1040     // null,
1041     // null,
1042     // null);
1043     // }
1044
1045     /**
1046      * Parse an entity from an OFMatchWithSwDpid.
1047      *
1048      * @param ofmWithSwDpid
1049      * @return the entity from the packet
1050      */
1051     // private Entity getEntityFromFlowMod(OFMatchWithSwDpid ofmWithSwDpid,
1052     // boolean isSource) {
1053     // byte[] dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerSource();
1054     // int nwSrc = ofmWithSwDpid.getOfMatch().getNetworkSource();
1055     // if (!isSource) {
1056     // dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerDestination();
1057     // nwSrc = ofmWithSwDpid.getOfMatch().getNetworkDestination();
1058     // }
1059     //
1060     // long dlAddr = Ethernet.toLong(dlAddrArr);
1061     //
1062     // // Ignore broadcast/multicast source
1063     // if ((dlAddrArr[0] & 0x1) != 0)
1064     // return null;
1065     //
1066     // Long swDpid = null;
1067     // Short inPort = null;
1068     //
1069     // if (isSource) {
1070     // swDpid = ofmWithSwDpid.getSwitchDataPathId();
1071     // inPort = ofmWithSwDpid.getOfMatch().getInputPort();
1072     // }
1073     //
1074     // /**for the new flow cache design, the flow mods retrived are not always
1075     // from the source, learn AP should be disabled --meiyang*/
1076     // boolean learnap = false;
1077     // /**
1078     // * if (swDpid == null ||
1079     // inPort == null ||
1080     // !isValidAttachmentPoint(swDpid, inPort)) {
1081     // // If this is an internal port or we otherwise don't want
1082     // // to learn on these ports. In the future, we should
1083     // // handle this case by labeling flows with something that
1084     // // will give us the entity class. For now, we'll do our
1085     // // best assuming attachment point information isn't used
1086     // // as a key field.
1087     // learnap = false;
1088     // }
1089     // */
1090     //
1091     // short vlan = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLan();
1092     // return new Entity(dlAddr,
1093     // ((vlan >= 0) ? vlan : null),
1094     // ((nwSrc != 0) ? nwSrc : null),
1095     // (learnap ? swDpid : null),
1096     // (learnap ? (int)inPort : null),
1097     // new Date());
1098     // }
1099
1100     /**
1101      * Look up a {@link Device} based on the provided {@link Entity}. We first
1102      * check the primary index. If we do not find an entry there we classify the
1103      * device into its IEntityClass and query the classIndex. This implies that
1104      * all key field of the current IEntityClassifier must be present in the
1105      * entity for the lookup to succeed!
1106      *
1107      * @param entity
1108      *            the entity to search for
1109      * @return The {@link Device} object if found
1110      */
1111     protected Device findDeviceByEntity(Entity entity) {
1112         // Look up the fully-qualified entity to see if it already
1113         // exists in the primary entity index.
1114         Long deviceKey = primaryIndex.findByEntity(entity);
1115         IEntityClass entityClass = null;
1116
1117         if (deviceKey == null) {
1118             // If the entity does not exist in the primary entity index,
1119             // use the entity classifier for find the classes for the
1120             // entity. Look up the entity in the returned class'
1121             // class entity index.
1122             entityClass = entityClassifier.classifyEntity(entity);
1123             if (entityClass == null) {
1124                 return null;
1125             }
1126             ClassState classState = getClassState(entityClass);
1127
1128             if (classState.classIndex != null) {
1129                 deviceKey = classState.classIndex.findByEntity(entity);
1130             }
1131         }
1132         if (deviceKey == null)
1133             return null;
1134         return deviceMap.get(deviceKey);
1135     }
1136
1137     /**
1138      * Get a destination device using entity fields that corresponds with the
1139      * given source device. The source device is important since there could be
1140      * ambiguity in the destination device without the attachment point
1141      * information.
1142      *
1143      * @param reference
1144      *            the source device's entity class. The returned destination
1145      *            will be in the same entity class as the source.
1146      * @param dstEntity
1147      *            the entity to look up
1148      * @return an {@link Device} or null if no device is found.
1149      */
1150     protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
1151
1152         // Look up the fully-qualified entity to see if it
1153         // exists in the primary entity index
1154         Long deviceKey = primaryIndex.findByEntity(dstEntity);
1155
1156         if (deviceKey == null) {
1157             // This could happen because:
1158             // 1) no destination known, or a broadcast destination
1159             // 2) if we have attachment point key fields since
1160             // attachment point information isn't available for
1161             // destination devices.
1162             // For the second case, we'll need to match up the
1163             // destination device with the class of the source
1164             // device.
1165             ClassState classState = getClassState(reference);
1166             if (classState.classIndex == null) {
1167                 return null;
1168             }
1169             deviceKey = classState.classIndex.findByEntity(dstEntity);
1170         }
1171         if (deviceKey == null)
1172             return null;
1173         return deviceMap.get(deviceKey);
1174     }
1175
1176     /**
1177      * Look up a {@link Device} within a particular entity class based on the
1178      * provided {@link Entity}.
1179      *
1180      * @param clazz
1181      *            the entity class to search for the entity
1182      * @param entity
1183      *            the entity to search for
1184      * @return The {@link Device} object if found private Device
1185      *         findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
1186      *         XXX - TODO throw new UnsupportedOperationException(); }
1187      */
1188
1189     /**
1190      * Look up a {@link Device} based on the provided {@link Entity}. Also
1191      * learns based on the new entity, and will update existing devices as
1192      * required.
1193      *
1194      * @param entity
1195      *            the {@link Entity}
1196      * @return The {@link Device} object if found
1197      */
1198     protected Device learnDeviceByEntity(Entity entity) {
1199         logger.info("Primary index {}", primaryIndex);
1200         ArrayList<Long> deleteQueue = null;
1201         LinkedList<DeviceUpdate> deviceUpdates = null;
1202         Device oldDevice = null;
1203         Device device = null;
1204
1205         // we may need to restart the learning process if we detect
1206         // concurrent modification. Note that we ensure that at least
1207         // one thread should always succeed so we don't get into infinite
1208         // starvation loops
1209         while (true) {
1210             deviceUpdates = null;
1211
1212             // Look up the fully-qualified entity to see if it already
1213             // exists in the primary entity index.
1214             Long deviceKey = primaryIndex.findByEntity(entity);
1215             IEntityClass entityClass = null;
1216
1217             if (deviceKey == null) {
1218                 // If the entity does not exist in the primary entity index,
1219                 // use the entity classifier for find the classes for the
1220                 // entity. Look up the entity in the returned class'
1221                 // class entity index.
1222                 entityClass = entityClassifier.classifyEntity(entity);
1223                 if (entityClass == null) {
1224                     // could not classify entity. No device
1225                     device = null;
1226                     break;
1227                 }
1228                 ClassState classState = getClassState(entityClass);
1229
1230                 if (classState.classIndex != null) {
1231                     deviceKey = classState.classIndex.findByEntity(entity);
1232                 }
1233             }
1234             if (deviceKey != null) {
1235                 // If the primary or secondary index contains the entity
1236                 // use resulting device key to look up the device in the
1237                 // device map, and use the referenced Device below.
1238                 device = deviceMap.get(deviceKey);
1239                 if (device == null) {
1240                     // This can happen due to concurrent modification
1241                     if (logger.isDebugEnabled()) {
1242                         logger.debug("No device for deviceKey {} while "
1243                                 + "while processing entity {}", deviceKey,
1244                                 entity);
1245                     }
1246                     // if so, then try again till we don't even get the device
1247                     // key
1248                     // and so we recreate the device
1249                     continue;
1250                 }
1251             } else {
1252                 // If the secondary index does not contain the entity,
1253                 // create a new Device object containing the entity, and
1254                 // generate a new device ID if the the entity is on an
1255                 // attachment point port. Otherwise ignore.
1256                 if (entity.hasSwitchPort()
1257                         && !isValidAttachmentPoint(entity.getPort())) {
1258                     // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
1259                     if (logger.isDebugEnabled()) {
1260                         logger.debug("Not learning new device on internal"
1261                                 + " link: {}", entity);
1262                     }
1263                     device = null;
1264                     break;
1265                 }
1266                 // Before we create the new device also check if
1267                 // the entity is allowed (e.g., for spoofing protection)
1268                 if (!isEntityAllowed(entity, entityClass)) {
1269                     // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1270                     if (logger.isDebugEnabled()) {
1271                         logger.debug("PacketIn is not allowed {} {}",
1272                                 entityClass.getName(), entity);
1273                     }
1274                     device = null;
1275                     break;
1276                 }
1277                 synchronized (deviceKeyLock) {
1278                     deviceKey = Long.valueOf(deviceKeyCounter++);
1279                 }
1280                 device = allocateDevice(deviceKey, entity, entityClass);
1281
1282                 // Add the new device to the primary map with a simple put
1283                 deviceMap.put(deviceKey, device);
1284
1285                 // update indices
1286                 if (!updateIndices(device, deviceKey)) {
1287                     if (deleteQueue == null)
1288                         deleteQueue = new ArrayList<Long>();
1289                     deleteQueue.add(deviceKey);
1290                     continue;
1291                 }
1292
1293                 updateSecondaryIndices(entity, entityClass, deviceKey);
1294
1295                 // We need to count and log here. If we log earlier we could
1296                 // hit a concurrent modification and restart the dev creation
1297                 // and potentially count the device twice.
1298                 // debugCounters.updateCounter(CNT_NEW_DEVICE);
1299                 if (logger.isDebugEnabled()) {
1300                     logger.debug(
1301                             "New device created: {} deviceKey={}, entity={}",
1302                             new Object[] { device, deviceKey, entity });
1303                 }
1304                 // generate new device update
1305                 deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
1306                         device, ADD, null));
1307
1308                 break;
1309             }
1310             // if it gets here, we have a pre-existing Device for this Entity
1311             if (!isEntityAllowed(entity, device.getEntityClass())) {
1312                 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1313                 if (logger.isDebugEnabled()) {
1314                     logger.info("PacketIn is not allowed {} {}", device
1315                             .getEntityClass().getName(), entity);
1316                 }
1317                 return null;
1318             }
1319             // If this is not an attachment point port we don't learn the new
1320             // entity
1321             // and don't update indexes. But we do allow the device to continue
1322             // up
1323             // the chain.
1324             if (entity.hasSwitchPort()
1325                     && !isValidAttachmentPoint(entity.getPort())) {
1326                 // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
1327                 break;
1328             }
1329             int entityindex = -1;
1330             if ((entityindex = device.entityIndex(entity)) >= 0) {
1331                 // Entity already exists
1332                 // update timestamp on the found entity
1333                 Date lastSeen = entity.getLastSeenTimestamp();
1334                 if (lastSeen == null) {
1335                     lastSeen = new Date();
1336                     entity.setLastSeenTimestamp(lastSeen);
1337                 }
1338                 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
1339                 // we break the loop after checking for changes to the AP
1340             } else {
1341                 // New entity for this device
1342                 // compute the insertion point for the entity.
1343                 // see Arrays.binarySearch()
1344                 entityindex = -(entityindex + 1);
1345                 Device newDevice = allocateDevice(device, entity, entityindex);
1346
1347                 // generate updates
1348                 EnumSet<DeviceField> changedFields = findChangedFields(device,
1349                         entity);
1350
1351                 // update the device map with a replace call
1352                 boolean res = deviceMap.replace(deviceKey, device, newDevice);
1353                 // If replace returns false, restart the process from the
1354                 // beginning (this implies another thread concurrently
1355                 // modified this Device).
1356                 if (!res)
1357                     continue;
1358                 oldDevice = device;
1359                 device = newDevice;
1360                 // update indices
1361                 if (!updateIndices(device, deviceKey)) {
1362                     continue;
1363                 }
1364                 updateSecondaryIndices(entity, device.getEntityClass(),
1365                         deviceKey);
1366
1367                 // We need to count here after all the possible "continue"
1368                 // statements in this branch
1369                 // debugCounters.updateCounter(CNT_NEW_ENTITY);
1370                 if (changedFields.size() > 0) {
1371                     // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1372                     deviceUpdates = updateUpdates(deviceUpdates,
1373                             new DeviceUpdate(newDevice, CHANGE, changedFields));
1374                 }
1375                 // we break the loop after checking for changed AP
1376             }
1377             // Update attachment point (will only be hit if the device
1378             // already existed and no concurrent modification)
1379             if (entity.hasSwitchPort()) {
1380                 boolean moved = device.updateAttachmentPoint(entity.getPort(),
1381                         entity.getLastSeenTimestamp().getTime());
1382                 // TODO: use update mechanism instead of sending the
1383                 // notification directly
1384                 if (moved) {
1385                     // we count device moved events in
1386                     // sendDeviceMovedNotification()
1387                     sendDeviceMovedNotification(device, oldDevice);
1388                     if (logger.isTraceEnabled()) {
1389                         logger.trace("Device moved: attachment points {},"
1390                                 + "entities {}", device.attachmentPoints,
1391                                 device.entities);
1392                     }
1393                 } else {
1394                     if (logger.isTraceEnabled()) {
1395                         logger.trace("Device attachment point updated: "
1396                                 + "attachment points {}," + "entities {}",
1397                                 device.attachmentPoints, device.entities);
1398                     }
1399                 }
1400             }
1401             break;
1402         }
1403
1404         if (deleteQueue != null) {
1405             for (Long l : deleteQueue) {
1406                 Device dev = deviceMap.get(l);
1407                 this.deleteDevice(dev);
1408             }
1409         }
1410
1411         processUpdates(deviceUpdates);
1412         // deviceSyncManager.storeDeviceThrottled(device);
1413
1414         return device;
1415     }
1416
1417     protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
1418         return true;
1419     }
1420
1421     protected EnumSet<DeviceField> findChangedFields(Device device,
1422             Entity newEntity) {
1423         EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
1424                 DeviceField.VLAN, DeviceField.SWITCHPORT);
1425
1426         if (newEntity.getIpv4Address() == null)
1427             changedFields.remove(DeviceField.IPV4);
1428         if (newEntity.getVlan() == null)
1429             changedFields.remove(DeviceField.VLAN);
1430         if (newEntity.getPort() == null)
1431             changedFields.remove(DeviceField.SWITCHPORT);
1432
1433         if (changedFields.size() == 0)
1434             return changedFields;
1435
1436         for (Entity entity : device.getEntities()) {
1437             if (newEntity.getIpv4Address() == null
1438                     || (entity.getIpv4Address() != null && entity
1439                             .getIpv4Address()
1440                             .equals(newEntity.getIpv4Address())))
1441                 changedFields.remove(DeviceField.IPV4);
1442             if (newEntity.getVlan() == null
1443                     || (entity.getVlan() != null && entity.getVlan().equals(
1444                             newEntity.getVlan())))
1445                 changedFields.remove(DeviceField.VLAN);
1446             if (newEntity.getPort() == null
1447                     || (entity.getPort() != null && entity.getPort().equals(
1448                             newEntity.getPort())))
1449                 changedFields.remove(DeviceField.SWITCHPORT);
1450         }
1451
1452         return changedFields;
1453     }
1454
1455     /**
1456      * Send update notifications to listeners
1457      *
1458      * @param updates
1459      *            the updates to process.
1460      */
1461     protected void processUpdates(Queue<DeviceUpdate> updates) {
1462         if (updates == null)
1463             return;
1464         DeviceUpdate update = null;
1465         while (null != (update = updates.poll())) {
1466             if (logger.isTraceEnabled()) {
1467                 logger.trace("Dispatching device update: {}", update);
1468             }
1469             // if (update.change == DeviceUpdate.Change.DELETE)
1470             // deviceSyncManager.removeDevice(update.device);
1471             // else
1472             // deviceSyncManager.storeDevice(update.device);
1473             List<IDeviceListener> listeners = deviceListeners
1474                     .getOrderedListeners();
1475             notifyListeners(listeners, update);
1476         }
1477     }
1478
1479     protected void notifyListeners(List<IDeviceListener> listeners,
1480             DeviceUpdate update) {
1481         // Topology update is for some reason outside of listeners registry
1482         // logic
1483         Entity[] ents = update.device.getEntities();
1484         Entity e = ents[ents.length - 1];
1485
1486         NodeConnector p = e.getPort();
1487         Node node = p.getNode();
1488         Host h = null;
1489         try {
1490
1491             byte[] mac = NetUtils.longToByteArray6(e.getMacAddress());
1492             DataLinkAddress dla = new EthernetAddress(
1493                     mac);
1494             e.getIpv4Address();
1495             InetAddress.getAllByName(e.getIpv4Address().toString());
1496             h = new org.opendaylight.controller.sal.core.Host(dla,
1497                     InetAddress.getByName(e.getIpv4Address().toString()));
1498         } catch (ConstructionException ce) {
1499             p = null;
1500             h = null;
1501         } catch (UnknownHostException ue) {
1502             p = null;
1503             h = null;
1504         }
1505
1506         if (topology != null && p != null && h != null) {
1507             if (update.change.equals(DeviceUpdate.Change.ADD)) {
1508                 Tier tier = new Tier(1);
1509                 switchManager.setNodeProp(node, tier);
1510                 topology.updateHostLink(p, h, UpdateType.ADDED, null);
1511             } else {
1512                 // No need to reset the tiering if no other hosts are currently
1513                 // connected
1514                 // If this switch was discovered to be an access switch, it
1515                 // still is even if the host is down
1516                 Tier tier = new Tier(0);
1517                 switchManager.setNodeProp(node, tier);
1518                 topology.updateHostLink(p, h, UpdateType.REMOVED, null);
1519             }
1520         }
1521
1522         if (listeners == null && newHostNotify.isEmpty()) {
1523             return;
1524         }
1525         /**
1526          * TODO: IfNewHostNotify is needed for current controller API. Adding
1527          * logic so that existing apps (like SimpleForwardingManager) work.
1528          * IDeviceListener adds additional methods and uses IListener's callback
1529          * ordering. The two interfaces need to be merged.
1530          */
1531
1532         for (IfNewHostNotify notify : newHostNotify) {
1533             switch (update.change) {
1534             case ADD:
1535                 notify.notifyHTClient(update.device.toHostNodeConnector());
1536                 break;
1537             case DELETE:
1538                 notify.notifyHTClientHostRemoved(update.device
1539                         .toHostNodeConnector());
1540                 break;
1541             case CHANGE:
1542             }
1543         }
1544
1545         /**
1546          * TODO: Remove this section as IDeviceListener functionality gets
1547          * merged with IfNewHostNotify
1548          */
1549         for (IDeviceListener listener : listeners) {
1550             switch (update.change) {
1551             case ADD:
1552                 listener.deviceAdded(update.device);
1553                 break;
1554             case DELETE:
1555                 listener.deviceRemoved(update.device);
1556                 break;
1557             case CHANGE:
1558                 for (DeviceField field : update.fieldsChanged) {
1559                     switch (field) {
1560                     case IPV4:
1561                         listener.deviceIPV4AddrChanged(update.device);
1562                         break;
1563                     case SWITCHPORT:
1564                         // listener.deviceMoved(update.device);
1565                         break;
1566                     case VLAN:
1567                         listener.deviceVlanChanged(update.device);
1568                         break;
1569                     default:
1570                         logger.debug("Unknown device field changed {}",
1571                                 update.fieldsChanged.toString());
1572                         break;
1573                     }
1574                 }
1575                 break;
1576             }
1577         }
1578     }
1579
1580     /**
1581      * Check if the entity e has all the keyFields set. Returns false if not
1582      *
1583      * @param e
1584      *            entity to check
1585      * @param keyFields
1586      *            the key fields to check e against
1587      * @return
1588      */
1589     protected boolean allKeyFieldsPresent(Entity e,
1590             EnumSet<DeviceField> keyFields) {
1591         for (DeviceField f : keyFields) {
1592             switch (f) {
1593             case MAC:
1594                 // MAC address is always present
1595                 break;
1596             case IPV4:
1597                 if (e.getIpv4Address() == null)
1598                     return false;
1599                 break;
1600             case SWITCHPORT:
1601                 if (e.getPort() == null)
1602                     return false;
1603                 break;
1604             case VLAN:
1605                 // FIXME: vlan==null is ambiguous: it can mean: not present
1606                 // or untagged
1607                 // if (e.vlan == null) return false;
1608                 break;
1609             default:
1610                 // we should never get here. unless somebody extended
1611                 // DeviceFields
1612                 throw new IllegalStateException();
1613             }
1614         }
1615         return true;
1616     }
1617
1618     private LinkedList<DeviceUpdate> updateUpdates(
1619             LinkedList<DeviceUpdate> list, DeviceUpdate update) {
1620         if (update == null)
1621             return list;
1622         if (list == null)
1623             list = new LinkedList<DeviceUpdate>();
1624         list.add(update);
1625
1626         return list;
1627     }
1628
1629     /**
1630      * Get the secondary index for a class. Will return null if the secondary
1631      * index was created concurrently in another thread.
1632      *
1633      * @param clazz
1634      *            the class for the index
1635      * @return
1636      */
1637     private ClassState getClassState(IEntityClass clazz) {
1638         ClassState classState = classStateMap.get(clazz.getName());
1639         if (classState != null)
1640             return classState;
1641
1642         classState = new ClassState(clazz);
1643         ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
1644         if (r != null) {
1645             // concurrent add
1646             return r;
1647         }
1648         return classState;
1649     }
1650
1651     /**
1652      * Update both the primary and class indices for the provided device. If the
1653      * update fails because of an concurrent update, will return false.
1654      *
1655      * @param device
1656      *            the device to update
1657      * @param deviceKey
1658      *            the device key for the device
1659      * @return true if the update succeeded, false otherwise.
1660      */
1661     private boolean updateIndices(Device device, Long deviceKey) {
1662         if (!primaryIndex.updateIndex(device, deviceKey)) {
1663             return false;
1664         }
1665         IEntityClass entityClass = device.getEntityClass();
1666         ClassState classState = getClassState(entityClass);
1667
1668         if (classState.classIndex != null) {
1669             if (!classState.classIndex.updateIndex(device, deviceKey))
1670                 return false;
1671         }
1672         return true;
1673     }
1674
1675     /**
1676      * Update the secondary indices for the given entity and associated entity
1677      * classes
1678      *
1679      * @param entity
1680      *            the entity to update
1681      * @param entityClass
1682      *            the entity class for the entity
1683      * @param deviceKey
1684      *            the device key to set up
1685      */
1686     private void updateSecondaryIndices(Entity entity,
1687             IEntityClass entityClass, Long deviceKey) {
1688         for (DeviceIndex index : secondaryIndexMap.values()) {
1689             index.updateIndex(entity, deviceKey);
1690         }
1691         ClassState state = getClassState(entityClass);
1692         for (DeviceIndex index : state.secondaryIndexMap.values()) {
1693             index.updateIndex(entity, deviceKey);
1694         }
1695     }
1696
1697     /**
1698      * Clean up expired entities/devices
1699      */
1700     protected void cleanupEntities() {
1701         // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
1702
1703         Calendar c = Calendar.getInstance();
1704         c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
1705         Date cutoff = c.getTime();
1706
1707         ArrayList<Entity> toRemove = new ArrayList<Entity>();
1708         ArrayList<Entity> toKeep = new ArrayList<Entity>();
1709
1710         Iterator<Device> diter = deviceMap.values().iterator();
1711         LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1712
1713         while (diter.hasNext()) {
1714             Device d = diter.next();
1715
1716             while (true) {
1717                 deviceUpdates.clear();
1718                 toRemove.clear();
1719                 toKeep.clear();
1720                 for (Entity e : d.getEntities()) {
1721                     if (e.getLastSeenTimestamp() != null
1722                             && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
1723                         // individual entity needs to be removed
1724                         toRemove.add(e);
1725                     } else {
1726                         toKeep.add(e);
1727                     }
1728                 }
1729                 if (toRemove.size() == 0) {
1730                     break;
1731                 }
1732
1733                 // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
1734                 for (Entity e : toRemove) {
1735                     removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
1736                             toKeep);
1737                 }
1738
1739                 if (toKeep.size() > 0) {
1740                     Device newDevice = allocateDevice(d.getDeviceKey(),
1741                             d.getDHCPClientName(), d.oldAPs,
1742                             d.attachmentPoints, toKeep, d.getEntityClass());
1743
1744                     EnumSet<DeviceField> changedFields = EnumSet
1745                             .noneOf(DeviceField.class);
1746                     for (Entity e : toRemove) {
1747                         changedFields.addAll(findChangedFields(newDevice, e));
1748                     }
1749                     DeviceUpdate update = null;
1750                     if (changedFields.size() > 0) {
1751                         update = new DeviceUpdate(d, CHANGE, changedFields);
1752                     }
1753
1754                     if (!deviceMap.replace(newDevice.getDeviceKey(), d,
1755                             newDevice)) {
1756                         // concurrent modification; try again
1757                         // need to use device that is the map now for the next
1758                         // iteration
1759                         d = deviceMap.get(d.getDeviceKey());
1760                         if (null != d)
1761                             continue;
1762                     }
1763                     if (update != null) {
1764                         // need to count after all possibly continue stmts in
1765                         // this branch
1766                         // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1767                         deviceUpdates.add(update);
1768                     }
1769                 } else {
1770                     DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
1771                     if (!deviceMap.remove(d.getDeviceKey(), d)) {
1772                         // concurrent modification; try again
1773                         // need to use device that is the map now for the next
1774                         // iteration
1775                         d = deviceMap.get(d.getDeviceKey());
1776                         if (null != d)
1777                             continue;
1778                         // debugCounters.updateCounter(CNT_DEVICE_DELETED);
1779                     }
1780                     deviceUpdates.add(update);
1781                 }
1782                 processUpdates(deviceUpdates);
1783                 break;
1784             }
1785         }
1786     }
1787
1788     protected void removeEntity(Entity removed, IEntityClass entityClass,
1789             Long deviceKey, Collection<Entity> others) {
1790         // Don't count in this method. This method CAN BE called to clean-up
1791         // after concurrent device adds/updates and thus counting here
1792         // is misleading
1793         for (DeviceIndex index : secondaryIndexMap.values()) {
1794             index.removeEntityIfNeeded(removed, deviceKey, others);
1795         }
1796         ClassState classState = getClassState(entityClass);
1797         for (DeviceIndex index : classState.secondaryIndexMap.values()) {
1798             index.removeEntityIfNeeded(removed, deviceKey, others);
1799         }
1800
1801         primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
1802
1803         if (classState.classIndex != null) {
1804             classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
1805                     others);
1806         }
1807     }
1808
1809     /**
1810      * method to delete a given device, remove all entities first and then
1811      * finally delete the device itself.
1812      *
1813      * @param device
1814      */
1815     protected void deleteDevice(Device device) {
1816         // Don't count in this method. This method CAN BE called to clean-up
1817         // after concurrent device adds/updates and thus counting here
1818         // is misleading
1819         ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
1820         for (Entity entity : device.getEntities()) {
1821             this.removeEntity(entity, device.getEntityClass(),
1822                     device.getDeviceKey(), emptyToKeep);
1823         }
1824         if (!deviceMap.remove(device.getDeviceKey(), device)) {
1825             if (logger.isDebugEnabled())
1826                 logger.debug("device map does not have this device -"
1827                         + device.toString());
1828         }
1829     }
1830
1831     private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
1832             Integer ipv4Address, NodeConnector port) {
1833         // FIXME: vlan==null is a valid search. Need to handle this
1834         // case correctly. Note that the code will still work correctly.
1835         // But we might do a full device search instead of using an index.
1836         EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
1837         if (macAddress != null)
1838             keys.add(DeviceField.MAC);
1839         if (vlan != null)
1840             keys.add(DeviceField.VLAN);
1841         if (ipv4Address != null)
1842             keys.add(DeviceField.IPV4);
1843         if (port != null)
1844             keys.add(DeviceField.SWITCHPORT);
1845         return keys;
1846     }
1847
1848     protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
1849             EnumSet<DeviceField> keyFields, Entity entity) {
1850         ClassState classState = getClassState(clazz);
1851         DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
1852         if (index == null)
1853             return Collections.<Device> emptySet().iterator();
1854         return new DeviceIndexInterator(this, index.queryByEntity(entity));
1855     }
1856
1857     protected Device allocateDevice(Long deviceKey, Entity entity,
1858             IEntityClass entityClass) {
1859         return new Device(this, deviceKey, entity, entityClass);
1860     }
1861
1862     // TODO: FIX THIS.
1863     protected Device allocateDevice(Long deviceKey, String dhcpClientName,
1864             List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
1865             Collection<Entity> entities, IEntityClass entityClass) {
1866         return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
1867                 entities, entityClass);
1868     }
1869
1870     protected Device allocateDevice(Device device, Entity entity,
1871             int insertionpoint) {
1872         return new Device(device, entity, insertionpoint);
1873     }
1874
1875     // not used
1876     protected Device allocateDevice(Device device, Set<Entity> entities) {
1877         List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
1878         List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
1879         for (Entity entity : entities) {
1880             if (entity.getPort() != null) {
1881                 AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
1882                 newPossibleAPs.add(aP);
1883             }
1884         }
1885         if (device.attachmentPoints != null) {
1886             for (AttachmentPoint oldAP : device.attachmentPoints) {
1887                 if (newPossibleAPs.contains(oldAP)) {
1888                     newAPs.add(oldAP);
1889                 }
1890             }
1891         }
1892         if (newAPs.isEmpty())
1893             newAPs = null;
1894         Device d = new Device(this, device.getDeviceKey(),
1895                 device.getDHCPClientName(), newAPs, null, entities,
1896                 device.getEntityClass());
1897         d.updateAttachmentPoint();
1898         return d;
1899     }
1900
1901     // *********************
1902     // ITopologyManagerAware
1903     // *********************
1904
1905     @Override
1906     public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
1907         Iterator<Device> diter = deviceMap.values().iterator();
1908
1909         while (diter.hasNext()) {
1910             Device d = diter.next();
1911             if (d.updateAttachmentPoint()) {
1912                 if (logger.isDebugEnabled()) {
1913                     logger.debug("Attachment point changed for device: {}", d);
1914                 }
1915                 sendDeviceMovedNotification(d);
1916             }
1917         }
1918     }
1919
1920     @Override
1921     public void edgeOverUtilized(Edge edge) {
1922         // nothing to do
1923     }
1924
1925     @Override
1926     public void edgeUtilBackToNormal(Edge edge) {
1927         // nothing to do
1928     }
1929
1930     // *********************
1931     // IEntityClassListener
1932     // *********************
1933
1934     @Override
1935     public void entityClassChanged(Set<String> entityClassNames) {
1936         /*
1937          * iterate through the devices, reclassify the devices that belong to
1938          * these entity class names
1939          */
1940         Iterator<Device> diter = deviceMap.values().iterator();
1941         while (diter.hasNext()) {
1942             Device d = diter.next();
1943             if (d.getEntityClass() == null
1944                     || entityClassNames.contains(d.getEntityClass().getName()))
1945                 reclassifyDevice(d);
1946         }
1947     }
1948
1949     // *************
1950     // Local methods
1951     // *************
1952     /**
1953      * Send update notifications to listeners
1954      *
1955      * @param updates
1956      *            the updates to process.
1957      */
1958     protected void sendDeviceMovedNotification(Device d) {
1959         // debugCounters.updateCounter(CNT_DEVICE_MOVED);
1960         // deviceSyncManager.storeDevice(d);
1961         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1962         if (listeners != null) {
1963             for (IDeviceListener listener : listeners) {
1964                 listener.deviceMoved(d);
1965             }
1966         }
1967     }
1968
1969     /**
1970      * Send update notifications to listeners. IfNewHostNotify listeners need to
1971      * remove old device and add new device.
1972      *
1973      * @param device
1974      * @param oldDevice
1975      */
1976     protected void sendDeviceMovedNotification(Device device, Device oldDevice) {
1977         for (IfNewHostNotify notify : newHostNotify) {
1978             notify.notifyHTClientHostRemoved(oldDevice.toHostNodeConnector());
1979             notify.notifyHTClient(device.toHostNodeConnector());
1980         }
1981         sendDeviceMovedNotification(device);
1982     }
1983
1984     /**
1985      * this method will reclassify and reconcile a device - possibilities are -
1986      * create new device(s), remove entities from this device. If the device
1987      * entity class did not change then it returns false else true.
1988      *
1989      * @param device
1990      */
1991     protected boolean reclassifyDevice(Device device) {
1992         // first classify all entities of this device
1993         if (device == null) {
1994             logger.debug("In reclassify for null device");
1995             return false;
1996         }
1997         boolean needToReclassify = false;
1998         for (Entity entity : device.entities) {
1999             IEntityClass entityClass = this.entityClassifier
2000                     .classifyEntity(entity);
2001             if (entityClass == null || device.getEntityClass() == null) {
2002                 needToReclassify = true;
2003                 break;
2004             }
2005             if (!entityClass.getName()
2006                     .equals(device.getEntityClass().getName())) {
2007                 needToReclassify = true;
2008                 break;
2009             }
2010         }
2011         if (needToReclassify == false) {
2012             return false;
2013         }
2014
2015         // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
2016         LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
2017         // delete this device and then re-learn all the entities
2018         this.deleteDevice(device);
2019         deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
2020                 null));
2021         if (!deviceUpdates.isEmpty())
2022             processUpdates(deviceUpdates);
2023         for (Entity entity : device.entities) {
2024             this.learnDeviceByEntity(entity);
2025         }
2026         return true;
2027     }
2028
2029     /**
2030      * For testing: sets the interval between writes of the same device to the
2031      * device store.
2032      *
2033      * @param intervalMs
2034      */
2035     // void setSyncStoreWriteInterval(int intervalMs) {
2036     // this.syncStoreWriteIntervalMs = intervalMs;
2037     // }
2038
2039     /**
2040      * For testing: sets the time between transition to MASTER and consolidate
2041      * store
2042      *
2043      * @param intervalMs
2044      */
2045     // void setInitialSyncStoreConsolidateMs(int intervalMs) {
2046     // this.initialSyncStoreConsolidateMs = intervalMs;
2047     // }
2048
2049     private long toLong(byte[] address) {
2050         long mac = 0;
2051         for (int i = 0; i < 6; i++) {
2052             long t = (address[i] & 0xffL) << ((5 - i) * 8);
2053             mac |= t;
2054         }
2055         return mac;
2056     }
2057
2058      /**
2059      * Accepts an IPv4 address in a byte array and returns the corresponding
2060      * 32-bit integer value.
2061      *
2062      * @param ipAddress
2063      * @return
2064      */
2065     private static int toIPv4Address(byte[] ipAddress) {
2066         int ip = 0;
2067         for (int i = 0; i < 4; i++) {
2068             int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
2069             ip |= t;
2070         }
2071         return ip;
2072     }
2073
2074     private void registerDeviceManagerDebugCounters() {
2075         /*
2076          * XXX Missing functionality if (debugCounters == null) {
2077          * logger.error("Debug Counter Service not found."); debugCounters = new
2078          * NullDebugCounter(); return; }
2079          * debugCounters.registerCounter(CNT_INCOMING,
2080          * "All incoming packets seen by this module",
2081          * CounterType.ALWAYS_COUNT);
2082          * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
2083          * "Number of flows that have been received for reconciliation by " +
2084          * "this module", CounterType.ALWAYS_COUNT);
2085          * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
2086          * "Number of flow reconcile events that failed because no source " +
2087          * "device could be identified", CounterType.WARN); // is this really a
2088          * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
2089          * "Number of flow reconcile events that failed because no " +
2090          * "destination device could be identified", CounterType.WARN); // is
2091          * this really a warning
2092          * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
2093          * "Number of packetIns that were discarded because the source " +
2094          * "MAC was broadcast or multicast", CounterType.WARN);
2095          * debugCounters.registerCounter(CNT_NO_SOURCE,
2096          * "Number of packetIns that were discarded because the " +
2097          * "could not identify a source device. This can happen if a " +
2098          * "packet is not allowed, appears on an illegal port, does not " +
2099          * "have a valid address space, etc.", CounterType.WARN);
2100          * debugCounters.registerCounter(CNT_NO_DEST,
2101          * "Number of packetIns that did not have an associated " +
2102          * "destination device. E.g., because the destination MAC is " +
2103          * "broadcast/multicast or is not yet known to the controller.",
2104          * CounterType.ALWAYS_COUNT);
2105          * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
2106          * "Number of times a DHCP client name was snooped from a " +
2107          * "packetIn.", CounterType.ALWAYS_COUNT);
2108          * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
2109          * "Number of times packetIn was received on an internal port and" +
2110          * "no source device is known for the source MAC. The packetIn is " +
2111          * "discarded.", CounterType.WARN);
2112          * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
2113          * "Number of times a packetIn was not allowed due to spoofing " +
2114          * "protection configuration.", CounterType.WARN); // is this really a
2115          * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
2116          * "Number of times a new device was learned",
2117          * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
2118          * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
2119          * "Number of times a packetIn was received on an internal port " +
2120          * "for a known device.", CounterType.ALWAYS_COUNT);
2121          * debugCounters.registerCounter(CNT_NEW_ENTITY,
2122          * "Number of times a new entity was learned for an existing device",
2123          * CounterType.ALWAYS_COUNT);
2124          * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
2125          * "Number of times device properties have changed",
2126          * CounterType.ALWAYS_COUNT);
2127          * debugCounters.registerCounter(CNT_DEVICE_MOVED,
2128          * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
2129          * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
2130          * "Number of times the entity cleanup task has been run",
2131          * CounterType.ALWAYS_COUNT);
2132          * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
2133          * "Number of times entities have been removed due to timeout " +
2134          * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
2135          * CounterType.ALWAYS_COUNT);
2136          * debugCounters.registerCounter(CNT_DEVICE_DELETED,
2137          * "Number of devices that have been removed due to inactivity",
2138          * CounterType.ALWAYS_COUNT);
2139          * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
2140          * "Number of devices that required reclassification and have been " +
2141          * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
2142          * debugCounters.registerCounter(CNT_DEVICE_STORED,
2143          * "Number of device entries written or updated to the sync store",
2144          * CounterType.ALWAYS_COUNT);
2145          * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
2146          * "Number of times a device update to the sync store was " +
2147          * "requested but not performed because the same device entities " +
2148          * "have recently been updated already", CounterType.ALWAYS_COUNT);
2149          * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
2150          * "Number of devices that were removed from the sync store " +
2151          * "because the local controller removed the device due to " +
2152          * "inactivity", CounterType.ALWAYS_COUNT);
2153          * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
2154          * "Number of times an operation on the sync store resulted in " +
2155          * "sync exception", CounterType.WARN); // it this an error?
2156          * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
2157          * "Number of devices that were read from the sync store after " +
2158          * "the local controller transitioned from SLAVE to MASTER",
2159          * CounterType.ALWAYS_COUNT);
2160          * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
2161          * "Number of times the task to consolidate entries in the " +
2162          * "store witch live known devices has been run",
2163          * CounterType.ALWAYS_COUNT);
2164          * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
2165          * "Number of times a device has been removed from the sync " +
2166          * "store because no corresponding live device is known. " +
2167          * "This indicates a remote controller still writing device " +
2168          * "entries despite the local controller being MASTER or an " +
2169          * "incosistent store update from the local controller.",
2170          * CounterType.WARN);
2171          * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
2172          * "Number of times this controller has transitioned from SLAVE " +
2173          * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
2174          */
2175     }
2176
2177     @Override
2178     public HostNodeConnector hostFind(InetAddress networkAddress) {
2179         // TODO Auto-generated method stub
2180         return null;
2181     }
2182
2183     @Override
2184     public HostNodeConnector hostQuery(InetAddress networkAddress) {
2185         // TODO Auto-generated method stub
2186         return null;
2187     }
2188
2189     @Override
2190     public Future<HostNodeConnector> discoverHost(InetAddress networkAddress) {
2191         // TODO Auto-generated method stub
2192         return null;
2193     }
2194
2195     @Override
2196     public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress) {
2197         // TODO Auto-generated method stub
2198         return null;
2199     }
2200
2201     @Override
2202     public Set<HostNodeConnector> getAllHosts() {
2203         Collection<Device> devices = Collections
2204                 .unmodifiableCollection(deviceMap.values());
2205         Iterator<Device> i = devices.iterator();
2206         Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2207         while (i.hasNext()) {
2208             Device device = i.next();
2209             nc.add(device.toHostNodeConnector());
2210         }
2211         return nc;
2212     }
2213
2214     @Override
2215     public Set<HostNodeConnector> getActiveStaticHosts() {
2216         Collection<Device> devices = Collections
2217                 .unmodifiableCollection(deviceMap.values());
2218         Iterator<Device> i = devices.iterator();
2219         Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2220         while (i.hasNext()) {
2221             Device device = i.next();
2222             if (device.isStaticHost())
2223                 nc.add(device.toHostNodeConnector());
2224         }
2225         return nc;
2226     }
2227
2228     @Override
2229     public Set<HostNodeConnector> getInactiveStaticHosts() {
2230         Collection<Entity> devices = Collections
2231                 .unmodifiableCollection(inactiveStaticDevices.values());
2232         Iterator<Entity> i = devices.iterator();
2233         Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2234         while (i.hasNext()) {
2235             Entity ent = i.next();
2236                 nc.add(ent.toHostNodeConnector());
2237
2238         }
2239         return nc;
2240     }
2241
2242     @Override
2243     public Status addStaticHost(String networkAddress, String dataLayerAddress,
2244             NodeConnector nc, String vlan) {
2245         Long mac = HexEncode.stringToLong(dataLayerAddress);
2246         try {
2247             InetAddress addr = InetAddress.getByName(networkAddress);
2248             int ip = toIPv4Address(addr.getAddress());
2249             Entity e = new Entity(mac, Short.valueOf(vlan), ip, nc, new Date());
2250
2251             if (switchManager.isNodeConnectorEnabled(e.getPort())) {
2252                 Device d = this.learnDeviceByEntity(e);
2253                 d.setStaticHost(true);
2254             } else {
2255                 logger.debug(
2256                         "Switch or switchport is not up, adding host {} to inactive list",
2257                         addr.getHostName());
2258                  inactiveStaticDevices.put(e.getPort(), e);
2259             }
2260             return new Status(StatusCode.SUCCESS);
2261         } catch (UnknownHostException e) {
2262             return new Status(StatusCode.INTERNALERROR);
2263         }
2264     }
2265
2266     @Override
2267     public Status removeStaticHost(String networkAddress) {
2268         Integer addr;
2269         try {
2270             addr = toIPv4Address(InetAddress.getByName(networkAddress)
2271                     .getAddress());
2272         } catch (UnknownHostException e) {
2273             return new Status(StatusCode.NOTFOUND, "Host does not exist");
2274         }
2275         Iterator<Device> di = this.getDeviceIteratorForQuery(null, null, addr,
2276                 null);
2277         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2278         while (di.hasNext()) {
2279             Device d = di.next();
2280             if (d.isStaticHost()) {
2281                 deleteDevice(d);
2282                 for (IfNewHostNotify notify : newHostNotify)
2283                     notify.notifyHTClientHostRemoved(d.toHostNodeConnector());
2284                 for (IDeviceListener listener : listeners)
2285                     listener.deviceRemoved(d);
2286             }
2287         }
2288         //go through inactive entites.
2289         Set<HostNodeConnector> inactive = this.getInactiveStaticHosts();
2290         for(HostNodeConnector nc : inactive){
2291             Integer ip =toIPv4Address(nc.getNetworkAddress().getAddress());
2292             if(ip.equals(addr)){
2293                 this.inactiveStaticDevices.remove(nc.getnodeConnector());
2294             }
2295         }
2296
2297
2298         return new Status(StatusCode.SUCCESS);
2299     }
2300
2301     @Override
2302     public void notifyNode(Node node, UpdateType type,
2303             Map<String, Property> propMap) {
2304         if (node == null)
2305             return;
2306         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2307         switch (type) {
2308         case REMOVED:
2309             logger.debug("Received removed node {}", node);
2310             for (Entry<Long, Device> d : deviceMap.entrySet()) {
2311                 Device device = d.getValue();
2312                 HostNodeConnector host = device.toHostNodeConnector();
2313                 if (host.getnodeconnectorNode().equals(node)) {
2314                     logger.debug("Node: {} is down, remove from Hosts_DB", node);
2315                     deleteDevice(device);
2316                     for (IfNewHostNotify notify : newHostNotify)
2317                         notify.notifyHTClientHostRemoved(host);
2318                     for (IDeviceListener listener : listeners)
2319                         listener.deviceRemoved(device);
2320                 }
2321             }
2322             break;
2323         default:
2324             break;
2325         }
2326     }
2327
2328     @Override
2329     public void notifyNodeConnector(NodeConnector nodeConnector,
2330             UpdateType type, Map<String, Property> propMap) {
2331         if (nodeConnector == null)
2332             return;
2333         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2334         boolean up = false;
2335         switch (type) {
2336         case ADDED:
2337             up = true;
2338             break;
2339         case REMOVED:
2340             break;
2341         case CHANGED:
2342             State state = (State) propMap.get(State.StatePropName);
2343             if ((state != null) && (state.getValue() == State.EDGE_UP)) {
2344                 up = true;
2345             }
2346             break;
2347         default:
2348             return;
2349         }
2350
2351         if (up) {
2352             logger.debug("handleNodeConnectorStatusUp {}", nodeConnector);
2353
2354             Entity ent = inactiveStaticDevices.get(nodeConnector);
2355             Device device = this.learnDeviceByEntity(ent);
2356             if(device!=null){
2357                 HostNodeConnector host = device.toHostNodeConnector();
2358                 if (host != null) {
2359                     inactiveStaticDevices.remove(nodeConnector);
2360                     for (IfNewHostNotify notify : newHostNotify)
2361                         notify.notifyHTClient(host);
2362                     for (IDeviceListener listener : listeners)
2363                         listener.deviceAdded(device);
2364                 } else {
2365                     logger.debug("handleNodeConnectorStatusDown {}", nodeConnector);
2366                 }
2367             }
2368         }else{
2369                 // remove all devices on the node that went down.
2370                 for (Entry<Long, Device> entry : deviceMap.entrySet()) {
2371                     Device device = entry.getValue();
2372                     HostNodeConnector host = device.toHostNodeConnector();
2373                     if (host.getnodeConnector().equals(nodeConnector)) {
2374                         deleteDevice(device);
2375                         for (IfNewHostNotify notify : newHostNotify)
2376                             notify.notifyHTClientHostRemoved(host);
2377                         for (IDeviceListener listener : listeners)
2378                             listener.deviceRemoved(device);
2379                     }
2380                 }
2381
2382             }
2383         }
2384
2385
2386     /**
2387      * For testing: consolidate the store NOW
2388      */
2389     // void scheduleConsolidateStoreNow() {
2390     // this.storeConsolidateTask.reschedule(0, TimeUnit.MILLISECONDS);
2391     // }
2392
2393     // private class DeviceSyncManager {
2394     // // maps (opaque) deviceKey to the time in System.nanoTime() when we
2395     // // last wrote the device to the sync store
2396     // private ConcurrentMap<Long, Long> lastWriteTimes =
2397     // new ConcurrentHashMap<Long, Long>();
2398     //
2399     // /**
2400     // * Write the given device to storage if we are MASTER.
2401     // * Use this method if the device has significantly changed (e.g.,
2402     // * new AP, new IP, entities removed).
2403     // * @param d the device to store
2404     // */
2405     // public void storeDevice(Device d) {
2406     // if (!isMaster)
2407     // return;
2408     // if (d == null)
2409     // return;
2410     // long now = System.nanoTime();
2411     // writeUpdatedDeviceToStorage(d);
2412     // lastWriteTimes.put(d.getDeviceKey(), now);
2413     // }
2414     //
2415     // /**
2416     // * Write the given device to storage if we are MASTER and if the
2417     // * last write for the device was more than this.syncStoreIntervalNs
2418     // * time ago.
2419     // * Use this method to updated last active times in the store.
2420     // * @param d the device to store
2421     // */
2422     // public void storeDeviceThrottled(Device d) {
2423     // long intervalNs = syncStoreWriteIntervalMs*1000L*1000L;
2424     // if (!isMaster)
2425     // return;
2426     // if (d == null)
2427     // return;
2428     // long now = System.nanoTime();
2429     // Long last = lastWriteTimes.get(d.getDeviceKey());
2430     // if (last == null ||
2431     // now - last > intervalNs) {
2432     // writeUpdatedDeviceToStorage(d);
2433     // lastWriteTimes.put(d.getDeviceKey(), now);
2434     // } else {
2435     // debugCounters.updateCounter(CNT_DEVICE_STORE_THROTTLED);
2436     // }
2437     // }
2438     //
2439     // /**
2440     // * Remove the given device from the store. If only some entities have
2441     // * been removed the updated device should be written using
2442     // * {@link #storeDevice(Device)}
2443     // * @param d
2444     // */
2445     // public void removeDevice(Device d) {
2446     // if (!isMaster)
2447     // return;
2448     // // FIXME: could we have a problem with concurrent put to the
2449     // // hashMap? I.e., we write a stale entry to the map after the
2450     // // delete and now are left with an entry we'll never clean up
2451     // lastWriteTimes.remove(d.getDeviceKey());
2452     // try {
2453     // // TODO: should probably do versioned delete. OTOH, even
2454     // // if we accidentally delete, we'll write it again after
2455     // // the next entity ....
2456     // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2457     // storeClient.delete(DeviceSyncRepresentation.computeKey(d));
2458     // } catch(ObsoleteVersionException e) {
2459     // // FIXME
2460     // } catch (SyncException e) {
2461     // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2462     // logger.error("Could not remove device " + d + " from store", e);
2463     // }
2464     // }
2465     //
2466     // /**
2467     // * Remove the given Versioned device from the store. If the device
2468     // * was locally modified ignore the delete request.
2469     // * @param syncedDeviceKey
2470     // */
2471     // private void removeDevice(Versioned<DeviceSyncRepresentation> dev) {
2472     // try {
2473     // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2474     // storeClient.delete(dev.getValue().getKey(),
2475     // dev.getVersion());
2476     // } catch(ObsoleteVersionException e) {
2477     // // Key was locally modified by another thread.
2478     // // Do not delete and ignore.
2479     // } catch(SyncException e) {
2480     // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2481     // logger.error("Failed to remove device entry for " +
2482     // dev.toString() + " from store.", e);
2483     // }
2484     // }
2485     //
2486     // /**
2487     // * Synchronously transition from SLAVE to MASTER. By iterating through
2488     // * the store and learning all devices from the store
2489     // */
2490     // private void goToMaster() {
2491     // if (logger.isDebugEnabled()) {
2492     // logger.debug("Transitioning to MASTER role");
2493     // }
2494     // debugCounters.updateCounter(CNT_TRANSITION_TO_MASTER);
2495     // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2496     // iter = null;
2497     // try {
2498     // iter = storeClient.entries();
2499     // } catch (SyncException e) {
2500     // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2501     // logger.error("Failed to read devices from sync store", e);
2502     // return;
2503     // }
2504     // try {
2505     // while(iter.hasNext()) {
2506     // Versioned<DeviceSyncRepresentation> versionedDevice =
2507     // iter.next().getValue();
2508     // DeviceSyncRepresentation storedDevice =
2509     // versionedDevice.getValue();
2510     // if (storedDevice == null)
2511     // continue;
2512     // debugCounters.updateCounter(CNT_DEVICES_FROM_STORE);
2513     // for(SyncEntity se: storedDevice.getEntities()) {
2514     // learnDeviceByEntity(se.asEntity());
2515     // }
2516     // }
2517     // } finally {
2518     // if (iter != null)
2519     // iter.close();
2520     // }
2521     // storeConsolidateTask.reschedule(initialSyncStoreConsolidateMs,
2522     // TimeUnit.MILLISECONDS);
2523     // }
2524     //
2525     // /**
2526     // * Actually perform the write of the device to the store
2527     // * FIXME: concurrent modification behavior
2528     // * @param device The device to write
2529     // */
2530     // private void writeUpdatedDeviceToStorage(Device device) {
2531     // try {
2532     // debugCounters.updateCounter(CNT_DEVICE_STORED);
2533     // // FIXME: use a versioned put
2534     // DeviceSyncRepresentation storeDevice =
2535     // new DeviceSyncRepresentation(device);
2536     // storeClient.put(storeDevice.getKey(), storeDevice);
2537     // } catch (ObsoleteVersionException e) {
2538     // // FIXME: what's the right behavior here. Can the store client
2539     // // even throw this error?
2540     // } catch (SyncException e) {
2541     // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2542     // logger.error("Could not write device " + device +
2543     // " to sync store:", e);
2544     // }
2545     // }
2546     //
2547     // /**
2548     // * Iterate through all entries in the sync store. For each device
2549     // * in the store check if any stored entity matches a live device. If
2550     // * no entities match a live device we remove the entry from the store.
2551     // *
2552     // * Note: we do not check if all devices known to device manager are
2553     // * in the store. We rely on regular packetIns for that.
2554     // * Note: it's possible that multiple entries in the store map to the
2555     // * same device. We don't check or handle this case.
2556     // *
2557     // * We need to perform this check after a SLAVE->MASTER transition to
2558     // * get rid of all entries the old master might have written to the
2559     // * store after we took over. We also run it regularly in MASTER
2560     // * state to ensure we don't have stale entries in the store
2561     // */
2562     // private void consolidateStore() {
2563     // if (!isMaster)
2564     // return;
2565     // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_RUNS);
2566     // if (logger.isDebugEnabled()) {
2567     // logger.debug("Running consolidateStore.");
2568     // }
2569     // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2570     // iter = null;
2571     // try {
2572     // iter = storeClient.entries();
2573     // } catch (SyncException e) {
2574     // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2575     // logger.error("Failed to read devices from sync store", e);
2576     // return;
2577     // }
2578     // try {
2579     // while(iter.hasNext()) {
2580     // boolean found = false;
2581     // Versioned<DeviceSyncRepresentation> versionedDevice =
2582     // iter.next().getValue();
2583     // DeviceSyncRepresentation storedDevice =
2584     // versionedDevice.getValue();
2585     // if (storedDevice == null)
2586     // continue;
2587     // for(SyncEntity se: storedDevice.getEntities()) {
2588     // try {
2589     // // Do we have a device for this entity??
2590     // IDevice d = findDevice(se.macAddress, se.vlan,
2591     // se.ipv4Address,
2592     // se.switchDPID,
2593     // se.switchPort);
2594     // if (d != null) {
2595     // found = true;
2596     // break;
2597     // }
2598     // } catch (IllegalArgumentException e) {
2599     // // not all key fields provided. Skip entity
2600     // }
2601     // }
2602     // if (!found) {
2603     // // We currently DO NOT have a live device that
2604     // // matches the current device from the store.
2605     // // Delete device from store.
2606     // if (logger.isDebugEnabled()) {
2607     // logger.debug("Removing device {} from store. No "
2608     // + "corresponding live device",
2609     // storedDevice.getKey());
2610     // }
2611     // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED);
2612     // removeDevice(versionedDevice);
2613     // }
2614     // }
2615     // } finally {
2616     // if (iter != null)
2617     // iter.close();
2618     // }
2619     // }
2620     // }
2621     //
2622     //
2623     // /**
2624     // * For testing. Sets the syncService. Only call after init but before
2625     // * startUp. Used by MockDeviceManager
2626     // * @param syncService
2627     // */
2628     // protected void setSyncServiceIfNotSet(ISyncService syncService) {
2629     // if (this.syncService == null)
2630     // this.syncService = syncService;
2631     // }
2632 }