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