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