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