2 * Copyright (c) 2011,2012 Big Switch Networks, Inc.
4 * Licensed under the Eclipse Public License, Version 1.0 (the
5 * "License"); you may not use this file except in compliance with the
6 * License. You may obtain a copy of the License at
8 * http://www.eclipse.org/legal/epl-v10.html
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 * implied. See the License for the specific language governing
14 * permissions and limitations under the License.
16 * This file incorporates work covered by the following copyright and
19 * Originally created by David Erickson, Stanford University
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the
23 * License. You may obtain a copy of the License at
25 * http://www.apache.org/licenses/LICENSE-2.0
27 * Unless required by applicable law or agreed to in writing,
28 * software distributed under the License is distributed on an "AS
29 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
30 * express or implied. See the License for the specific language
31 * governing permissions and limitations under the License.
34 package org.opendaylight.controller.hosttracker.internal;
36 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.ADD;
37 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.CHANGE;
38 import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.DELETE;
40 import java.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;
55 import java.util.Map.Entry;
56 import java.util.Queue;
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;
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;
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
116 public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
117 IListenDataPacket, ITopologyManagerAware, IfIptoHost,
119 protected static Logger logger = LoggerFactory
120 .getLogger(DeviceManagerImpl.class);
122 public static final String MODULE_NAME = "devicemanager";
124 // protected ITopologyService topology;
125 // protected IStorageSourceService storageSource;
126 // protected IRestApiService restApi;
127 // protected IThreadPoolService threadPool;
128 // protected IFlowReconcileService flowReconcileMgr;
129 // protected IFlowReconcileEngineService flowReconcileEngine;
130 // protected IDebugCounterService debugCounters;
131 // private ISyncService syncService;
132 // private IStoreClient<String,DeviceSyncRepresentation> storeClient;
133 // private DeviceSyncManager deviceSyncManager;
135 private ITopologyManager topology;
136 private ISwitchManager switchManager = null;
137 private IDataPacketService dataPacketService = null;
139 public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
140 public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
141 + "-reconcileRequest";
142 public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
143 + "-reconcileNoSourceDevice";
144 public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
145 + "-reconcileNoDestDevice";
146 public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
147 + "-broadcastSource";
148 public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
149 public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
150 public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
151 + "-dhcpClientNameSnooped";
152 public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
153 + "-deviceOnInternalPortNotLearned";
154 public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
155 + "-packetNotAllowed";
156 public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
157 public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
158 + "-packetOnInternalPortForKnownDevice";
159 public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
160 public static final String CNT_DEVICE_CHANGED = MODULE_NAME
162 public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
163 public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
164 + "-cleanupEntitiesRuns";
165 public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
166 + "-entityRemovedTimeout";
167 public static final String CNT_DEVICE_DELETED = MODULE_NAME
169 public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
170 + "-deviceReclassifyDelete";
171 public static final String CNT_DEVICE_STORED = MODULE_NAME
173 public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
174 + "-deviceStoreThrottled";
175 public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
176 + "-deviceRemovedFromStore";
177 public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
179 public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
180 + "-devicesFromStore";
181 public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
182 + "-consolidateStoreRuns";
183 public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
184 + "-consolidateStoreDevicesRemoved";
186 static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
187 .getCanonicalName() + ".stateStore";
190 * Time interval between writes of entries for the same device to the sync
193 // static final int DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS =
194 // 5*60*1000; // 5 min
195 // private int syncStoreWriteIntervalMs =
196 // DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS;
199 * Time after SLAVE->MASTER until we run the consolidate store code.
201 // static final int DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS =
202 // 15*1000; // 15 sec
203 // private int initialSyncStoreConsolidateMs =
204 // DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS;
207 * Time interval between consolidate store runs.
209 // static final int DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS =
210 // 75*60*1000; // 75 min
211 // private final int syncStoreConsolidateIntervalMs =
212 // DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS;
215 * Time in milliseconds before entities will expire
217 protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
220 * Time in seconds between cleaning up old entities/devices
222 protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
225 * This is the master device map that maps device IDs to {@link Device}
228 protected ConcurrentHashMap<Long, Device> deviceMap;
230 protected ConcurrentHashMap<NodeConnector, Entity> inactiveStaticDevices;
232 * Counter used to generate device keys
234 protected long deviceKeyCounter = 0;
237 * Lock for incrementing the device key counter
239 protected Object deviceKeyLock = new Object();
242 * This is the primary entity index that contains all entities
244 protected DeviceUniqueIndex primaryIndex;
247 * This stores secondary indices over the fields in the devices
249 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
252 * This map contains state for each of the {@ref IEntityClass} that exist
254 protected ConcurrentHashMap<String, ClassState> classStateMap;
257 * This is the list of indices we want on a per-class basis
259 protected Set<EnumSet<DeviceField>> perClassIndices;
262 * The entity classifier currently in use
264 protected IEntityClassifierService entityClassifier;
267 * Used to cache state about specific entity classes
269 protected class ClassState {
274 protected DeviceUniqueIndex classIndex;
277 * This stores secondary indices over the fields in the device for the
280 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
283 * Allocate a new {@link ClassState} object for the class
286 * the class to use for the state
288 public ClassState(IEntityClass clazz) {
289 EnumSet<DeviceField> keyFields = clazz.getKeyFields();
290 EnumSet<DeviceField> primaryKeyFields = entityClassifier
292 boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
294 if (!keyFieldsMatchPrimary)
295 classIndex = new DeviceUniqueIndex(keyFields);
297 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
298 for (EnumSet<DeviceField> fields : perClassIndices) {
299 secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
305 * Device manager event listeners reclassifyDeviceListeners are notified
306 * first before reconcileDeviceListeners. This is to make sure devices are
307 * correctly reclassified before reconciliation.
309 protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
312 * Using the IfNewHostNotify to notify listeners of host changes.
314 private Set<IfNewHostNotify> newHostNotify = Collections
315 .synchronizedSet(new HashSet<IfNewHostNotify>());
318 * A device update event to be dispatched
320 protected static class DeviceUpdate {
326 * The affected device
328 protected Device device;
331 * The change that was made
333 protected Change change;
336 * If not added, then this is the list of fields changed
338 protected EnumSet<DeviceField> fieldsChanged;
340 public DeviceUpdate(Device device, Change change,
341 EnumSet<DeviceField> fieldsChanged) {
343 this.device = device;
344 this.change = change;
345 this.fieldsChanged = fieldsChanged;
349 public String toString() {
350 String devIdStr = device.getEntityClass().getName() + "::"
351 + device.getMACAddressString();
352 return "DeviceUpdate [device=" + devIdStr + ", change=" + change
353 + ", fieldsChanged=" + fieldsChanged + "]";
359 * AttachmentPointComparator
361 * Compares two attachment points and returns the latest one. It is assumed
362 * that the two attachment points are in the same L2 domain.
366 protected class AttachmentPointComparator implements
367 Comparator<AttachmentPoint> {
368 public AttachmentPointComparator() {
373 public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
374 // First compare based on L2 domain ID;
376 // XXX - missing functionality -- need topology
377 // long oldDomain = topology.getL2DomainId(oldSw);
378 // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
380 boolean oldBD = false;
382 // XXX - missing functionality -- need topology
383 // long newDomain = topology.getL2DomainId(newSw);
384 // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
386 boolean newBD = false;
388 if (oldDomain < newDomain)
390 else if (oldDomain > newDomain)
393 // Give preference to OFPP_LOCAL always
394 if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
395 && newAP.getPort().getType()
396 .equals(NodeConnectorIDType.SWSTACK)) {
398 } else if (oldAP.getPort().getType()
399 .equals(NodeConnectorIDType.SWSTACK)
400 && !newAP.getPort().getType()
401 .equals(NodeConnectorIDType.SWSTACK)) {
405 // We expect that the last seen of the new AP is higher than
406 // old AP, if it is not, just reverse and send the negative
408 if (oldAP.getActiveSince() > newAP.getActiveSince())
409 return -compare(newAP, oldAP);
411 long activeOffset = 0;
412 // XXX - missing functionality -- need topology
413 // if (!topology.isConsistent(oldSw, oldPort, newSw, newPort)) {
414 if (!newBD && oldBD) {
417 if (newBD && oldBD) {
418 activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
419 } else if (newBD && !oldBD) {
420 activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
424 // // The attachment point is consistent.
425 // activeOffset = AttachmentPoint.CONSISTENT_TIMEOUT;
428 if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
429 || (newAP.getLastSeen() > oldAP.getLastSeen()
430 + AttachmentPoint.INACTIVITY_INTERVAL)) {
438 * Comparator for sorting by cluster ID
440 public AttachmentPointComparator apComparator;
443 * Switch ports where attachment points shouldn't be learned
445 private Set<SwitchPort> suppressAPs;
448 * Periodic task to clean up expired entities
450 public SingletonTask entityCleanupTask;
452 // ********************
453 // Dependency injection
454 // ********************
456 void setNewHostNotify(IfNewHostNotify obj) {
457 this.newHostNotify.add(obj);
460 void unsetNewHostNotify(IfNewHostNotify obj) {
461 this.newHostNotify.remove(obj);
464 void setDataPacketService(IDataPacketService s) {
465 this.dataPacketService = s;
468 void unsetDataPacketService(IDataPacketService s) {
469 if (this.dataPacketService == s) {
470 this.dataPacketService = null;
474 public void setTopologyManager(ITopologyManager s) {
478 public void unsetTopologyManager(ITopologyManager s) {
479 if (this.topology == s) {
480 logger.debug("Topology Manager Service removed!");
481 this.topology = null;
485 private volatile boolean stopped = true;
486 private ScheduledExecutorService ses;
494 public void start() {
495 this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
497 // XXX - TODO need to make it possible to register a non-default
499 entityClassifier = new DefaultEntityClassifier();
500 this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
501 this.suppressAPs = Collections
502 .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
503 primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
504 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
506 deviceMap = new ConcurrentHashMap<Long, Device>();
507 inactiveStaticDevices = new ConcurrentHashMap<NodeConnector, Entity>();
508 classStateMap = new ConcurrentHashMap<String, ClassState>();
509 apComparator = new AttachmentPointComparator();
511 addIndex(true, EnumSet.of(DeviceField.IPV4));
513 // floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
514 // floodlightProvider.addHAListener(this.haListenerDelegate);
515 // if (topology != null)
516 // topology.addListener(this);
517 // flowReconcileMgr.addFlowReconcileListener(this);
518 // entityClassifier.addListener(this);
521 // XXX - Should use a common threadpool but this doesn't currently exist
522 ses = Executors.newScheduledThreadPool(1);
523 Runnable ecr = new Runnable() {
528 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
532 entityCleanupTask = new SingletonTask(ses, ecr);
533 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
536 * XXX Missing functionality if (restApi != null) {
537 * restApi.addRestletRoutable(new DeviceRoutable()); } else {
538 * logger.debug("Could not instantiate REST API"); }
541 registerDeviceManagerDebugCounters();
544 * XXX Missing functionality try {
545 * this.syncService.registerStore(DEVICE_SYNC_STORE_NAME, Scope.LOCAL);
546 * this.storeClient = this.syncService
547 * .getStoreClient(DEVICE_SYNC_STORE_NAME, String.class,
548 * DeviceSyncRepresentation.class); } catch (SyncException e) { throw
549 * new FloodlightModuleException("Error while setting up sync service",
552 * Runnable consolidateStoreRunner = new Runnable() {
554 * @Override public void run() { deviceSyncManager.consolidateStore();
555 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
556 * TimeUnit.MILLISECONDS); debugCounters.flushCounters(); } };
557 * storeConsolidateTask = new SingletonTask(ses,
558 * consolidateStoreRunner); if (isMaster)
559 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
560 * TimeUnit.MILLISECONDS);
565 * Periodic task to consolidate entries in the store. I.e., delete entries
566 * in the store that are not known to DeviceManager
568 // XXX - Missing functionality
569 // private SingletonTask storeConsolidateTask;
571 // *********************
572 // IDeviceManagerService
573 // *********************
575 void setSwitchManager(ISwitchManager s) {
576 logger.debug("SwitchManager set");
577 this.switchManager = s;
580 void unsetSwitchManager(ISwitchManager s) {
581 if (this.switchManager == s) {
582 logger.debug("SwitchManager removed!");
583 this.switchManager = null;
588 public IDevice getDevice(Long deviceKey) {
589 return deviceMap.get(deviceKey);
593 public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
594 NodeConnector port) throws IllegalArgumentException {
595 if (vlan != null && vlan.shortValue() <= 0)
597 if (ipv4Address != null && ipv4Address == 0)
599 Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
600 if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
601 throw new IllegalArgumentException("Not all key fields specified."
602 + " Required fields: " + entityClassifier.getKeyFields());
604 return findDeviceByEntity(e);
608 public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
609 Short vlan, Integer ipv4Address) throws IllegalArgumentException {
610 if (vlan != null && vlan.shortValue() <= 0)
612 if (ipv4Address != null && ipv4Address == 0)
614 Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
615 if (entityClass == null
616 || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
617 throw new IllegalArgumentException("Not all key fields and/or "
618 + " no source device specified. Required fields: "
619 + entityClassifier.getKeyFields());
621 return findDestByEntity(entityClass, e);
625 public Collection<? extends IDevice> getAllDevices() {
626 return Collections.unmodifiableCollection(deviceMap.values());
630 public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
632 perClassIndices.add(keyFields);
634 secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
639 public Iterator<? extends IDevice> queryDevices(Long macAddress,
640 Short vlan, Integer ipv4Address, NodeConnector port) {
641 DeviceIndex index = null;
642 if (secondaryIndexMap.size() > 0) {
643 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
645 index = secondaryIndexMap.get(keys);
648 Iterator<Device> deviceIterator = null;
650 // Do a full table scan
651 deviceIterator = deviceMap.values().iterator();
654 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
655 vlan, ipv4Address, port, null);
656 deviceIterator = new DeviceIndexInterator(this,
657 index.queryByEntity(entity));
660 DeviceIterator di = new DeviceIterator(deviceIterator, null,
661 macAddress, vlan, ipv4Address, port);
666 public Iterator<? extends IDevice> queryClassDevices(
667 IEntityClass entityClass, Long macAddress, Short vlan,
668 Integer ipv4Address, NodeConnector port) {
669 ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
670 ClassState classState = getClassState(entityClass);
672 DeviceIndex index = null;
673 if (classState.secondaryIndexMap.size() > 0) {
674 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
676 index = classState.secondaryIndexMap.get(keys);
679 Iterator<Device> iter;
681 index = classState.classIndex;
684 return new DeviceIterator(deviceMap.values().iterator(),
685 new IEntityClass[] { entityClass }, macAddress, vlan,
688 // scan the entire class
689 iter = new DeviceIndexInterator(this, index.getAll());
693 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
694 vlan, ipv4Address, port, null);
695 iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
699 return new MultiIterator<Device>(iterators.iterator());
702 protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
703 Short vlan, Integer ipv4Address, NodeConnector port) {
704 DeviceIndex index = null;
705 if (secondaryIndexMap.size() > 0) {
706 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
708 index = secondaryIndexMap.get(keys);
711 Iterator<Device> deviceIterator = null;
713 // Do a full table scan
714 deviceIterator = deviceMap.values().iterator();
717 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
718 vlan, ipv4Address, port, null);
719 deviceIterator = new DeviceIndexInterator(this,
720 index.queryByEntity(entity));
723 DeviceIterator di = new DeviceIterator(deviceIterator, null,
724 macAddress, vlan, ipv4Address, port);
729 public void addListener(IDeviceListener listener) {
730 deviceListeners.addListener("device", listener);
735 public void addSuppressAPs(NodeConnector port) {
736 suppressAPs.add(new SwitchPort(port));
740 public void removeSuppressAPs(NodeConnector port) {
741 suppressAPs.remove(new SwitchPort(port));
745 public Set<SwitchPort> getSuppressAPs() {
746 return Collections.unmodifiableSet(suppressAPs);
749 private void logListeners() {
750 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
751 if (listeners != null) {
752 StringBuffer sb = new StringBuffer();
753 sb.append("DeviceListeners: ");
754 for (IDeviceListener l : listeners) {
755 sb.append(l.getName());
758 logger.debug(sb.toString());
763 // IFlowReconcileListener
766 * XXX - Missing functionality
768 * @Override public Command reconcileFlows(ArrayList<OFMatchReconcile>
769 * ofmRcList) { ListIterator<OFMatchReconcile> iter =
770 * ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm =
773 * // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) {
776 * if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { return
779 * protected Command reconcileFlow(OFMatchReconcile ofm) {
780 * debugCounters.updateCounter(CNT_RECONCILE_REQUEST); // Extract source
781 * entity information Entity srcEntity =
782 * getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) {
783 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
786 * // Find the device by source entity Device srcDevice =
787 * findDeviceByEntity(srcEntity); if (srcDevice == null) {
788 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
789 * Command.STOP; } // Store the source device in the context
790 * fcStore.put(ofm.cntx, CONTEXT_SRC_DEVICE, srcDevice);
792 * // Find the device matching the destination from the entity // classes of
793 * the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid,
794 * false); Device dstDevice = null; if (dstEntity != null) { dstDevice =
795 * findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice !=
796 * null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else
797 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } else {
798 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } if
799 * (logger.isTraceEnabled()) {
800 * logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " +
801 * "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
802 * srcEntity, srcDevice, dstEntity, dstDevice } ); } return
803 * Command.CONTINUE; }
811 public PacketResult receiveDataPacket(RawPacket inPkt) {
812 // XXX - Can this really pass in null? Why would you ever want that?
814 return PacketResult.IGNORED;
817 // throw new Exception("Sample");
818 // } catch (Exception e) {
819 // logger.error("Sample stack trace", e);
822 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
824 if (formattedPak instanceof Ethernet) {
825 eth = (Ethernet) formattedPak;
827 return PacketResult.IGNORED;
830 // Extract source entity information
831 NodeConnector inPort = inPkt.getIncomingNodeConnector();
832 Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
833 if (srcEntity == null) {
834 // debugCounters.updateCounter(CNT_BROADCAST_SOURCE);
835 return PacketResult.CONSUME;
838 // Learn from ARP packet for special VRRP settings.
839 // In VRRP settings, the source MAC address and sender MAC
840 // addresses can be different. In such cases, we need to learn
841 // the IP to MAC mapping of the VRRP IP address. The source
842 // entity will not have that information. Hence, a separate call
843 // to learn devices in such cases.
844 learnDeviceFromArpResponseData(eth, inPort);
846 // Learn/lookup device information
847 Device srcDevice = learnDeviceByEntity(srcEntity);
848 if (srcDevice == null) {
849 // debugCounters.updateCounter(CNT_NO_SOURCE);
850 return PacketResult.CONSUME;
852 logger.trace("Saw packet from device {}", srcDevice);
854 // // Store the source device in the context
855 // fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice);
857 // // Find the device matching the destination from the entity
858 // // classes of the source.
859 // Entity dstEntity = getDestEntityFromPacket(eth);
860 // Device dstDevice = null;
861 // if (dstEntity != null) {
863 // findDestByEntity(srcDevice.getEntityClass(), dstEntity);
864 // if (dstDevice != null)
865 // fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice);
867 // //debugCounters.updateCounter(CNT_NO_DEST);
869 // //debugCounters.updateCounter(CNT_NO_DEST);
872 // if (logger.isTraceEnabled()) {
873 // logger.trace("Received PI: {} on switch {}, port {} *** eth={}" +
874 // " *** srcDev={} *** dstDev={} *** ",
875 // new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
876 // srcDevice, dstDevice });
879 // snoopDHCPClientName(eth, srcDevice);
881 return PacketResult.KEEP_PROCESSING;
889 * Snoop and record client-provided host name from DHCP requests
894 // private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
895 // if (! (eth.getPayload() instanceof IPv4) )
897 // IPv4 ipv4 = (IPv4) eth.getPayload();
898 // if (! (ipv4.getPayload() instanceof UDP) )
900 // UDP udp = (UDP) ipv4.getPayload();
901 // if (!(udp.getPayload() instanceof DHCP))
903 // DHCP dhcp = (DHCP) udp.getPayload();
904 // byte opcode = dhcp.getOpCode();
905 // if (opcode == DHCP.OPCODE_REQUEST) {
906 // DHCPOption dhcpOption = dhcp.getOption(
907 // DHCPOptionCode.OptionCode_Hostname);
908 // if (dhcpOption != null) {
909 // debugCounters.updateCounter(CNT_DHCP_CLIENT_NAME_SNOOPED);
910 // srcDevice.dhcpClientName = new String(dhcpOption.getData());
916 * Check whether the given attachment point is valid given the current
923 * @return true if it's a valid attachment point
925 public boolean isValidAttachmentPoint(NodeConnector port) {
926 // XXX - missing functionality -- need topology module
927 // if (topology.isAttachmentPointPort(port) == false)
929 if (topology.isInternal(port))
931 if (!switchManager.isNodeConnectorEnabled(port))
933 if (suppressAPs.contains(new SwitchPort(port)))
940 * Get sender IP address from packet if the packet is either an ARP packet.
946 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
947 if (eth.getPayload() instanceof ARP) {
948 ARP arp = (ARP) eth.getPayload();
949 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
950 && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
951 return toIPv4Address(arp.getSenderProtocolAddress());
958 * Parse an entity from an {@link Ethernet} packet.
961 * the packet to parse
963 * the switch on which the packet arrived
965 * the original packetin
966 * @return the entity from the packet
968 protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
969 byte[] dlAddrArr = eth.getSourceMACAddress();
970 long dlAddr = toLong(dlAddrArr);
972 // Ignore broadcast/multicast source
973 if ((dlAddrArr[0] & 0x1) != 0)
976 // XXX missing functionality
978 int nwSrc = getSrcNwAddr(eth, dlAddr);
979 return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
984 * Learn device from ARP data in scenarios where the Ethernet source MAC is
985 * different from the sender hardware address in ARP data.
987 protected void learnDeviceFromArpResponseData(Ethernet eth,
988 NodeConnector port) {
990 if (!(eth.getPayload() instanceof ARP))
992 ARP arp = (ARP) eth.getPayload();
994 byte[] dlAddrArr = eth.getSourceMACAddress();
995 long dlAddr = toLong(dlAddrArr);
997 byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
998 long senderAddr = toLong(senderHardwareAddr);
1000 if (dlAddr == senderAddr)
1003 // Ignore broadcast/multicast source
1004 if ((senderHardwareAddr[0] & 0x1) != 0)
1007 // short vlan = eth.getVlanID();
1008 int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
1010 Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
1013 learnDeviceByEntity(e);
1017 * Get a (partial) entity for the destination from the packet.
1022 // protected Entity getDestEntityFromPacket(Ethernet eth) {
1023 // byte[] dlAddrArr = eth.getDestinationMACAddress();
1024 // long dlAddr = Ethernet.toLong(dlAddrArr);
1025 // short vlan = eth.getVlanID();
1028 // // Ignore broadcast/multicast destination
1029 // if ((dlAddrArr[0] & 0x1) != 0)
1032 // if (eth.getPayload() instanceof IPv4) {
1033 // IPv4 ipv4 = (IPv4) eth.getPayload();
1034 // nwDst = ipv4.getDestinationAddress();
1037 // return new Entity(dlAddr,
1038 // ((vlan >= 0) ? vlan : null),
1039 // ((nwDst != 0) ? nwDst : null),
1046 * Parse an entity from an OFMatchWithSwDpid.
1048 * @param ofmWithSwDpid
1049 * @return the entity from the packet
1051 // private Entity getEntityFromFlowMod(OFMatchWithSwDpid ofmWithSwDpid,
1052 // boolean isSource) {
1053 // byte[] dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerSource();
1054 // int nwSrc = ofmWithSwDpid.getOfMatch().getNetworkSource();
1056 // dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerDestination();
1057 // nwSrc = ofmWithSwDpid.getOfMatch().getNetworkDestination();
1060 // long dlAddr = Ethernet.toLong(dlAddrArr);
1062 // // Ignore broadcast/multicast source
1063 // if ((dlAddrArr[0] & 0x1) != 0)
1066 // Long swDpid = null;
1067 // Short inPort = null;
1070 // swDpid = ofmWithSwDpid.getSwitchDataPathId();
1071 // inPort = ofmWithSwDpid.getOfMatch().getInputPort();
1074 // /**for the new flow cache design, the flow mods retrived are not always
1075 // from the source, learn AP should be disabled --meiyang*/
1076 // boolean learnap = false;
1078 // * if (swDpid == null ||
1079 // inPort == null ||
1080 // !isValidAttachmentPoint(swDpid, inPort)) {
1081 // // If this is an internal port or we otherwise don't want
1082 // // to learn on these ports. In the future, we should
1083 // // handle this case by labeling flows with something that
1084 // // will give us the entity class. For now, we'll do our
1085 // // best assuming attachment point information isn't used
1086 // // as a key field.
1091 // short vlan = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLan();
1092 // return new Entity(dlAddr,
1093 // ((vlan >= 0) ? vlan : null),
1094 // ((nwSrc != 0) ? nwSrc : null),
1095 // (learnap ? swDpid : null),
1096 // (learnap ? (int)inPort : null),
1101 * Look up a {@link Device} based on the provided {@link Entity}. We first
1102 * check the primary index. If we do not find an entry there we classify the
1103 * device into its IEntityClass and query the classIndex. This implies that
1104 * all key field of the current IEntityClassifier must be present in the
1105 * entity for the lookup to succeed!
1108 * the entity to search for
1109 * @return The {@link Device} object if found
1111 protected Device findDeviceByEntity(Entity entity) {
1112 // Look up the fully-qualified entity to see if it already
1113 // exists in the primary entity index.
1114 Long deviceKey = primaryIndex.findByEntity(entity);
1115 IEntityClass entityClass = null;
1117 if (deviceKey == null) {
1118 // If the entity does not exist in the primary entity index,
1119 // use the entity classifier for find the classes for the
1120 // entity. Look up the entity in the returned class'
1121 // class entity index.
1122 entityClass = entityClassifier.classifyEntity(entity);
1123 if (entityClass == null) {
1126 ClassState classState = getClassState(entityClass);
1128 if (classState.classIndex != null) {
1129 deviceKey = classState.classIndex.findByEntity(entity);
1132 if (deviceKey == null)
1134 return deviceMap.get(deviceKey);
1138 * Get a destination device using entity fields that corresponds with the
1139 * given source device. The source device is important since there could be
1140 * ambiguity in the destination device without the attachment point
1144 * the source device's entity class. The returned destination
1145 * will be in the same entity class as the source.
1147 * the entity to look up
1148 * @return an {@link Device} or null if no device is found.
1150 protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
1152 // Look up the fully-qualified entity to see if it
1153 // exists in the primary entity index
1154 Long deviceKey = primaryIndex.findByEntity(dstEntity);
1156 if (deviceKey == null) {
1157 // This could happen because:
1158 // 1) no destination known, or a broadcast destination
1159 // 2) if we have attachment point key fields since
1160 // attachment point information isn't available for
1161 // destination devices.
1162 // For the second case, we'll need to match up the
1163 // destination device with the class of the source
1165 ClassState classState = getClassState(reference);
1166 if (classState.classIndex == null) {
1169 deviceKey = classState.classIndex.findByEntity(dstEntity);
1171 if (deviceKey == null)
1173 return deviceMap.get(deviceKey);
1177 * Look up a {@link Device} within a particular entity class based on the
1178 * provided {@link Entity}.
1181 * the entity class to search for the entity
1183 * the entity to search for
1184 * @return The {@link Device} object if found private Device
1185 * findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
1186 * XXX - TODO throw new UnsupportedOperationException(); }
1190 * Look up a {@link Device} based on the provided {@link Entity}. Also
1191 * learns based on the new entity, and will update existing devices as
1195 * the {@link Entity}
1196 * @return The {@link Device} object if found
1198 protected Device learnDeviceByEntity(Entity entity) {
1199 logger.info("Primary index {}", primaryIndex);
1200 ArrayList<Long> deleteQueue = null;
1201 LinkedList<DeviceUpdate> deviceUpdates = null;
1202 Device oldDevice = null;
1203 Device device = null;
1205 // we may need to restart the learning process if we detect
1206 // concurrent modification. Note that we ensure that at least
1207 // one thread should always succeed so we don't get into infinite
1210 deviceUpdates = null;
1212 // Look up the fully-qualified entity to see if it already
1213 // exists in the primary entity index.
1214 Long deviceKey = primaryIndex.findByEntity(entity);
1215 IEntityClass entityClass = null;
1217 if (deviceKey == null) {
1218 // If the entity does not exist in the primary entity index,
1219 // use the entity classifier for find the classes for the
1220 // entity. Look up the entity in the returned class'
1221 // class entity index.
1222 entityClass = entityClassifier.classifyEntity(entity);
1223 if (entityClass == null) {
1224 // could not classify entity. No device
1228 ClassState classState = getClassState(entityClass);
1230 if (classState.classIndex != null) {
1231 deviceKey = classState.classIndex.findByEntity(entity);
1234 if (deviceKey != null) {
1235 // If the primary or secondary index contains the entity
1236 // use resulting device key to look up the device in the
1237 // device map, and use the referenced Device below.
1238 device = deviceMap.get(deviceKey);
1239 if (device == null) {
1240 // This can happen due to concurrent modification
1241 if (logger.isDebugEnabled()) {
1242 logger.debug("No device for deviceKey {} while "
1243 + "while processing entity {}", deviceKey,
1246 // if so, then try again till we don't even get the device
1248 // and so we recreate the device
1252 // If the secondary index does not contain the entity,
1253 // create a new Device object containing the entity, and
1254 // generate a new device ID if the the entity is on an
1255 // attachment point port. Otherwise ignore.
1256 if (entity.hasSwitchPort()
1257 && !isValidAttachmentPoint(entity.getPort())) {
1258 // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
1259 if (logger.isDebugEnabled()) {
1260 logger.debug("Not learning new device on internal"
1261 + " link: {}", entity);
1266 // Before we create the new device also check if
1267 // the entity is allowed (e.g., for spoofing protection)
1268 if (!isEntityAllowed(entity, entityClass)) {
1269 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1270 if (logger.isDebugEnabled()) {
1271 logger.debug("PacketIn is not allowed {} {}",
1272 entityClass.getName(), entity);
1277 synchronized (deviceKeyLock) {
1278 deviceKey = Long.valueOf(deviceKeyCounter++);
1280 device = allocateDevice(deviceKey, entity, entityClass);
1282 // Add the new device to the primary map with a simple put
1283 deviceMap.put(deviceKey, device);
1286 if (!updateIndices(device, deviceKey)) {
1287 if (deleteQueue == null)
1288 deleteQueue = new ArrayList<Long>();
1289 deleteQueue.add(deviceKey);
1293 updateSecondaryIndices(entity, entityClass, deviceKey);
1295 // We need to count and log here. If we log earlier we could
1296 // hit a concurrent modification and restart the dev creation
1297 // and potentially count the device twice.
1298 // debugCounters.updateCounter(CNT_NEW_DEVICE);
1299 if (logger.isDebugEnabled()) {
1301 "New device created: {} deviceKey={}, entity={}",
1302 new Object[] { device, deviceKey, entity });
1304 // generate new device update
1305 deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
1306 device, ADD, null));
1310 // if it gets here, we have a pre-existing Device for this Entity
1311 if (!isEntityAllowed(entity, device.getEntityClass())) {
1312 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1313 if (logger.isDebugEnabled()) {
1314 logger.info("PacketIn is not allowed {} {}", device
1315 .getEntityClass().getName(), entity);
1319 // If this is not an attachment point port we don't learn the new
1321 // and don't update indexes. But we do allow the device to continue
1324 if (entity.hasSwitchPort()
1325 && !isValidAttachmentPoint(entity.getPort())) {
1326 // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
1329 int entityindex = -1;
1330 if ((entityindex = device.entityIndex(entity)) >= 0) {
1331 // Entity already exists
1332 // update timestamp on the found entity
1333 Date lastSeen = entity.getLastSeenTimestamp();
1334 if (lastSeen == null) {
1335 lastSeen = new Date();
1336 entity.setLastSeenTimestamp(lastSeen);
1338 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
1339 // we break the loop after checking for changes to the AP
1341 // New entity for this device
1342 // compute the insertion point for the entity.
1343 // see Arrays.binarySearch()
1344 entityindex = -(entityindex + 1);
1345 Device newDevice = allocateDevice(device, entity, entityindex);
1348 EnumSet<DeviceField> changedFields = findChangedFields(device,
1351 // update the device map with a replace call
1352 boolean res = deviceMap.replace(deviceKey, device, newDevice);
1353 // If replace returns false, restart the process from the
1354 // beginning (this implies another thread concurrently
1355 // modified this Device).
1361 if (!updateIndices(device, deviceKey)) {
1364 updateSecondaryIndices(entity, device.getEntityClass(),
1367 // We need to count here after all the possible "continue"
1368 // statements in this branch
1369 // debugCounters.updateCounter(CNT_NEW_ENTITY);
1370 if (changedFields.size() > 0) {
1371 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1372 deviceUpdates = updateUpdates(deviceUpdates,
1373 new DeviceUpdate(newDevice, CHANGE, changedFields));
1375 // we break the loop after checking for changed AP
1377 // Update attachment point (will only be hit if the device
1378 // already existed and no concurrent modification)
1379 if (entity.hasSwitchPort()) {
1380 boolean moved = device.updateAttachmentPoint(entity.getPort(),
1381 entity.getLastSeenTimestamp().getTime());
1382 // TODO: use update mechanism instead of sending the
1383 // notification directly
1385 // we count device moved events in
1386 // sendDeviceMovedNotification()
1387 sendDeviceMovedNotification(device, oldDevice);
1388 if (logger.isTraceEnabled()) {
1389 logger.trace("Device moved: attachment points {},"
1390 + "entities {}", device.attachmentPoints,
1394 if (logger.isTraceEnabled()) {
1395 logger.trace("Device attachment point updated: "
1396 + "attachment points {}," + "entities {}",
1397 device.attachmentPoints, device.entities);
1404 if (deleteQueue != null) {
1405 for (Long l : deleteQueue) {
1406 Device dev = deviceMap.get(l);
1407 this.deleteDevice(dev);
1411 processUpdates(deviceUpdates);
1412 // deviceSyncManager.storeDeviceThrottled(device);
1417 protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
1421 protected EnumSet<DeviceField> findChangedFields(Device device,
1423 EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
1424 DeviceField.VLAN, DeviceField.SWITCHPORT);
1426 if (newEntity.getIpv4Address() == null)
1427 changedFields.remove(DeviceField.IPV4);
1428 if (newEntity.getVlan() == null)
1429 changedFields.remove(DeviceField.VLAN);
1430 if (newEntity.getPort() == null)
1431 changedFields.remove(DeviceField.SWITCHPORT);
1433 if (changedFields.size() == 0)
1434 return changedFields;
1436 for (Entity entity : device.getEntities()) {
1437 if (newEntity.getIpv4Address() == null
1438 || (entity.getIpv4Address() != null && entity
1440 .equals(newEntity.getIpv4Address())))
1441 changedFields.remove(DeviceField.IPV4);
1442 if (newEntity.getVlan() == null
1443 || (entity.getVlan() != null && entity.getVlan().equals(
1444 newEntity.getVlan())))
1445 changedFields.remove(DeviceField.VLAN);
1446 if (newEntity.getPort() == null
1447 || (entity.getPort() != null && entity.getPort().equals(
1448 newEntity.getPort())))
1449 changedFields.remove(DeviceField.SWITCHPORT);
1452 return changedFields;
1456 * Send update notifications to listeners
1459 * the updates to process.
1461 protected void processUpdates(Queue<DeviceUpdate> updates) {
1462 if (updates == null)
1464 DeviceUpdate update = null;
1465 while (null != (update = updates.poll())) {
1466 if (logger.isTraceEnabled()) {
1467 logger.trace("Dispatching device update: {}", update);
1469 // if (update.change == DeviceUpdate.Change.DELETE)
1470 // deviceSyncManager.removeDevice(update.device);
1472 // deviceSyncManager.storeDevice(update.device);
1473 List<IDeviceListener> listeners = deviceListeners
1474 .getOrderedListeners();
1475 notifyListeners(listeners, update);
1479 protected void notifyListeners(List<IDeviceListener> listeners,
1480 DeviceUpdate update) {
1481 // Topology update is for some reason outside of listeners registry
1483 Entity[] ents = update.device.getEntities();
1484 Entity e = ents[ents.length - 1];
1486 NodeConnector p = e.getPort();
1487 Node node = p.getNode();
1491 byte[] mac = NetUtils.longToByteArray6(e.getMacAddress());
1492 DataLinkAddress dla = new EthernetAddress(
1495 InetAddress.getAllByName(e.getIpv4Address().toString());
1496 h = new org.opendaylight.controller.sal.core.Host(dla,
1497 InetAddress.getByName(e.getIpv4Address().toString()));
1498 } catch (ConstructionException ce) {
1501 } catch (UnknownHostException ue) {
1506 if (topology != null && p != null && h != null) {
1507 if (update.change.equals(DeviceUpdate.Change.ADD)) {
1508 Tier tier = new Tier(1);
1509 switchManager.setNodeProp(node, tier);
1510 topology.updateHostLink(p, h, UpdateType.ADDED, null);
1512 // No need to reset the tiering if no other hosts are currently
1514 // If this switch was discovered to be an access switch, it
1515 // still is even if the host is down
1516 Tier tier = new Tier(0);
1517 switchManager.setNodeProp(node, tier);
1518 topology.updateHostLink(p, h, UpdateType.REMOVED, null);
1522 if (listeners == null && newHostNotify.isEmpty()) {
1526 * TODO: IfNewHostNotify is needed for current controller API. Adding
1527 * logic so that existing apps (like SimpleForwardingManager) work.
1528 * IDeviceListener adds additional methods and uses IListener's callback
1529 * ordering. The two interfaces need to be merged.
1532 for (IfNewHostNotify notify : newHostNotify) {
1533 switch (update.change) {
1535 notify.notifyHTClient(update.device.toHostNodeConnector());
1538 notify.notifyHTClientHostRemoved(update.device
1539 .toHostNodeConnector());
1546 * TODO: Remove this section as IDeviceListener functionality gets
1547 * merged with IfNewHostNotify
1549 for (IDeviceListener listener : listeners) {
1550 switch (update.change) {
1552 listener.deviceAdded(update.device);
1555 listener.deviceRemoved(update.device);
1558 for (DeviceField field : update.fieldsChanged) {
1561 listener.deviceIPV4AddrChanged(update.device);
1564 // listener.deviceMoved(update.device);
1567 listener.deviceVlanChanged(update.device);
1570 logger.debug("Unknown device field changed {}",
1571 update.fieldsChanged.toString());
1581 * Check if the entity e has all the keyFields set. Returns false if not
1586 * the key fields to check e against
1589 protected boolean allKeyFieldsPresent(Entity e,
1590 EnumSet<DeviceField> keyFields) {
1591 for (DeviceField f : keyFields) {
1594 // MAC address is always present
1597 if (e.getIpv4Address() == null)
1601 if (e.getPort() == null)
1605 // FIXME: vlan==null is ambiguous: it can mean: not present
1607 // if (e.vlan == null) return false;
1610 // we should never get here. unless somebody extended
1612 throw new IllegalStateException();
1618 private LinkedList<DeviceUpdate> updateUpdates(
1619 LinkedList<DeviceUpdate> list, DeviceUpdate update) {
1623 list = new LinkedList<DeviceUpdate>();
1630 * Get the secondary index for a class. Will return null if the secondary
1631 * index was created concurrently in another thread.
1634 * the class for the index
1637 private ClassState getClassState(IEntityClass clazz) {
1638 ClassState classState = classStateMap.get(clazz.getName());
1639 if (classState != null)
1642 classState = new ClassState(clazz);
1643 ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
1652 * Update both the primary and class indices for the provided device. If the
1653 * update fails because of an concurrent update, will return false.
1656 * the device to update
1658 * the device key for the device
1659 * @return true if the update succeeded, false otherwise.
1661 private boolean updateIndices(Device device, Long deviceKey) {
1662 if (!primaryIndex.updateIndex(device, deviceKey)) {
1665 IEntityClass entityClass = device.getEntityClass();
1666 ClassState classState = getClassState(entityClass);
1668 if (classState.classIndex != null) {
1669 if (!classState.classIndex.updateIndex(device, deviceKey))
1676 * Update the secondary indices for the given entity and associated entity
1680 * the entity to update
1681 * @param entityClass
1682 * the entity class for the entity
1684 * the device key to set up
1686 private void updateSecondaryIndices(Entity entity,
1687 IEntityClass entityClass, Long deviceKey) {
1688 for (DeviceIndex index : secondaryIndexMap.values()) {
1689 index.updateIndex(entity, deviceKey);
1691 ClassState state = getClassState(entityClass);
1692 for (DeviceIndex index : state.secondaryIndexMap.values()) {
1693 index.updateIndex(entity, deviceKey);
1698 * Clean up expired entities/devices
1700 protected void cleanupEntities() {
1701 // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
1703 Calendar c = Calendar.getInstance();
1704 c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
1705 Date cutoff = c.getTime();
1707 ArrayList<Entity> toRemove = new ArrayList<Entity>();
1708 ArrayList<Entity> toKeep = new ArrayList<Entity>();
1710 Iterator<Device> diter = deviceMap.values().iterator();
1711 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1713 while (diter.hasNext()) {
1714 Device d = diter.next();
1717 deviceUpdates.clear();
1720 for (Entity e : d.getEntities()) {
1721 if (e.getLastSeenTimestamp() != null
1722 && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
1723 // individual entity needs to be removed
1729 if (toRemove.size() == 0) {
1733 // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
1734 for (Entity e : toRemove) {
1735 removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
1739 if (toKeep.size() > 0) {
1740 Device newDevice = allocateDevice(d.getDeviceKey(),
1741 d.getDHCPClientName(), d.oldAPs,
1742 d.attachmentPoints, toKeep, d.getEntityClass());
1744 EnumSet<DeviceField> changedFields = EnumSet
1745 .noneOf(DeviceField.class);
1746 for (Entity e : toRemove) {
1747 changedFields.addAll(findChangedFields(newDevice, e));
1749 DeviceUpdate update = null;
1750 if (changedFields.size() > 0) {
1751 update = new DeviceUpdate(d, CHANGE, changedFields);
1754 if (!deviceMap.replace(newDevice.getDeviceKey(), d,
1756 // concurrent modification; try again
1757 // need to use device that is the map now for the next
1759 d = deviceMap.get(d.getDeviceKey());
1763 if (update != null) {
1764 // need to count after all possibly continue stmts in
1766 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1767 deviceUpdates.add(update);
1770 DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
1771 if (!deviceMap.remove(d.getDeviceKey(), d)) {
1772 // concurrent modification; try again
1773 // need to use device that is the map now for the next
1775 d = deviceMap.get(d.getDeviceKey());
1778 // debugCounters.updateCounter(CNT_DEVICE_DELETED);
1780 deviceUpdates.add(update);
1782 processUpdates(deviceUpdates);
1788 protected void removeEntity(Entity removed, IEntityClass entityClass,
1789 Long deviceKey, Collection<Entity> others) {
1790 // Don't count in this method. This method CAN BE called to clean-up
1791 // after concurrent device adds/updates and thus counting here
1793 for (DeviceIndex index : secondaryIndexMap.values()) {
1794 index.removeEntityIfNeeded(removed, deviceKey, others);
1796 ClassState classState = getClassState(entityClass);
1797 for (DeviceIndex index : classState.secondaryIndexMap.values()) {
1798 index.removeEntityIfNeeded(removed, deviceKey, others);
1801 primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
1803 if (classState.classIndex != null) {
1804 classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
1810 * method to delete a given device, remove all entities first and then
1811 * finally delete the device itself.
1815 protected void deleteDevice(Device device) {
1816 // Don't count in this method. This method CAN BE called to clean-up
1817 // after concurrent device adds/updates and thus counting here
1819 ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
1820 for (Entity entity : device.getEntities()) {
1821 this.removeEntity(entity, device.getEntityClass(),
1822 device.getDeviceKey(), emptyToKeep);
1824 if (!deviceMap.remove(device.getDeviceKey(), device)) {
1825 if (logger.isDebugEnabled())
1826 logger.debug("device map does not have this device -"
1827 + device.toString());
1831 private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
1832 Integer ipv4Address, NodeConnector port) {
1833 // FIXME: vlan==null is a valid search. Need to handle this
1834 // case correctly. Note that the code will still work correctly.
1835 // But we might do a full device search instead of using an index.
1836 EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
1837 if (macAddress != null)
1838 keys.add(DeviceField.MAC);
1840 keys.add(DeviceField.VLAN);
1841 if (ipv4Address != null)
1842 keys.add(DeviceField.IPV4);
1844 keys.add(DeviceField.SWITCHPORT);
1848 protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
1849 EnumSet<DeviceField> keyFields, Entity entity) {
1850 ClassState classState = getClassState(clazz);
1851 DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
1853 return Collections.<Device> emptySet().iterator();
1854 return new DeviceIndexInterator(this, index.queryByEntity(entity));
1857 protected Device allocateDevice(Long deviceKey, Entity entity,
1858 IEntityClass entityClass) {
1859 return new Device(this, deviceKey, entity, entityClass);
1863 protected Device allocateDevice(Long deviceKey, String dhcpClientName,
1864 List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
1865 Collection<Entity> entities, IEntityClass entityClass) {
1866 return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
1867 entities, entityClass);
1870 protected Device allocateDevice(Device device, Entity entity,
1871 int insertionpoint) {
1872 return new Device(device, entity, insertionpoint);
1876 protected Device allocateDevice(Device device, Set<Entity> entities) {
1877 List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
1878 List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
1879 for (Entity entity : entities) {
1880 if (entity.getPort() != null) {
1881 AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
1882 newPossibleAPs.add(aP);
1885 if (device.attachmentPoints != null) {
1886 for (AttachmentPoint oldAP : device.attachmentPoints) {
1887 if (newPossibleAPs.contains(oldAP)) {
1892 if (newAPs.isEmpty())
1894 Device d = new Device(this, device.getDeviceKey(),
1895 device.getDHCPClientName(), newAPs, null, entities,
1896 device.getEntityClass());
1897 d.updateAttachmentPoint();
1901 // *********************
1902 // ITopologyManagerAware
1903 // *********************
1906 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
1907 Iterator<Device> diter = deviceMap.values().iterator();
1909 while (diter.hasNext()) {
1910 Device d = diter.next();
1911 if (d.updateAttachmentPoint()) {
1912 if (logger.isDebugEnabled()) {
1913 logger.debug("Attachment point changed for device: {}", d);
1915 sendDeviceMovedNotification(d);
1921 public void edgeOverUtilized(Edge edge) {
1926 public void edgeUtilBackToNormal(Edge edge) {
1930 // *********************
1931 // IEntityClassListener
1932 // *********************
1935 public void entityClassChanged(Set<String> entityClassNames) {
1937 * iterate through the devices, reclassify the devices that belong to
1938 * these entity class names
1940 Iterator<Device> diter = deviceMap.values().iterator();
1941 while (diter.hasNext()) {
1942 Device d = diter.next();
1943 if (d.getEntityClass() == null
1944 || entityClassNames.contains(d.getEntityClass().getName()))
1945 reclassifyDevice(d);
1953 * Send update notifications to listeners
1956 * the updates to process.
1958 protected void sendDeviceMovedNotification(Device d) {
1959 // debugCounters.updateCounter(CNT_DEVICE_MOVED);
1960 // deviceSyncManager.storeDevice(d);
1961 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1962 if (listeners != null) {
1963 for (IDeviceListener listener : listeners) {
1964 listener.deviceMoved(d);
1970 * Send update notifications to listeners. IfNewHostNotify listeners need to
1971 * remove old device and add new device.
1976 protected void sendDeviceMovedNotification(Device device, Device oldDevice) {
1977 for (IfNewHostNotify notify : newHostNotify) {
1978 notify.notifyHTClientHostRemoved(oldDevice.toHostNodeConnector());
1979 notify.notifyHTClient(device.toHostNodeConnector());
1981 sendDeviceMovedNotification(device);
1985 * this method will reclassify and reconcile a device - possibilities are -
1986 * create new device(s), remove entities from this device. If the device
1987 * entity class did not change then it returns false else true.
1991 protected boolean reclassifyDevice(Device device) {
1992 // first classify all entities of this device
1993 if (device == null) {
1994 logger.debug("In reclassify for null device");
1997 boolean needToReclassify = false;
1998 for (Entity entity : device.entities) {
1999 IEntityClass entityClass = this.entityClassifier
2000 .classifyEntity(entity);
2001 if (entityClass == null || device.getEntityClass() == null) {
2002 needToReclassify = true;
2005 if (!entityClass.getName()
2006 .equals(device.getEntityClass().getName())) {
2007 needToReclassify = true;
2011 if (needToReclassify == false) {
2015 // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
2016 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
2017 // delete this device and then re-learn all the entities
2018 this.deleteDevice(device);
2019 deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
2021 if (!deviceUpdates.isEmpty())
2022 processUpdates(deviceUpdates);
2023 for (Entity entity : device.entities) {
2024 this.learnDeviceByEntity(entity);
2030 * For testing: sets the interval between writes of the same device to the
2035 // void setSyncStoreWriteInterval(int intervalMs) {
2036 // this.syncStoreWriteIntervalMs = intervalMs;
2040 * For testing: sets the time between transition to MASTER and consolidate
2045 // void setInitialSyncStoreConsolidateMs(int intervalMs) {
2046 // this.initialSyncStoreConsolidateMs = intervalMs;
2049 private long toLong(byte[] address) {
2051 for (int i = 0; i < 6; i++) {
2052 long t = (address[i] & 0xffL) << ((5 - i) * 8);
2059 * Accepts an IPv4 address in a byte array and returns the corresponding
2060 * 32-bit integer value.
2065 private static int toIPv4Address(byte[] ipAddress) {
2067 for (int i = 0; i < 4; i++) {
2068 int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
2074 private void registerDeviceManagerDebugCounters() {
2076 * XXX Missing functionality if (debugCounters == null) {
2077 * logger.error("Debug Counter Service not found."); debugCounters = new
2078 * NullDebugCounter(); return; }
2079 * debugCounters.registerCounter(CNT_INCOMING,
2080 * "All incoming packets seen by this module",
2081 * CounterType.ALWAYS_COUNT);
2082 * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
2083 * "Number of flows that have been received for reconciliation by " +
2084 * "this module", CounterType.ALWAYS_COUNT);
2085 * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
2086 * "Number of flow reconcile events that failed because no source " +
2087 * "device could be identified", CounterType.WARN); // is this really a
2088 * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
2089 * "Number of flow reconcile events that failed because no " +
2090 * "destination device could be identified", CounterType.WARN); // is
2091 * this really a warning
2092 * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
2093 * "Number of packetIns that were discarded because the source " +
2094 * "MAC was broadcast or multicast", CounterType.WARN);
2095 * debugCounters.registerCounter(CNT_NO_SOURCE,
2096 * "Number of packetIns that were discarded because the " +
2097 * "could not identify a source device. This can happen if a " +
2098 * "packet is not allowed, appears on an illegal port, does not " +
2099 * "have a valid address space, etc.", CounterType.WARN);
2100 * debugCounters.registerCounter(CNT_NO_DEST,
2101 * "Number of packetIns that did not have an associated " +
2102 * "destination device. E.g., because the destination MAC is " +
2103 * "broadcast/multicast or is not yet known to the controller.",
2104 * CounterType.ALWAYS_COUNT);
2105 * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
2106 * "Number of times a DHCP client name was snooped from a " +
2107 * "packetIn.", CounterType.ALWAYS_COUNT);
2108 * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
2109 * "Number of times packetIn was received on an internal port and" +
2110 * "no source device is known for the source MAC. The packetIn is " +
2111 * "discarded.", CounterType.WARN);
2112 * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
2113 * "Number of times a packetIn was not allowed due to spoofing " +
2114 * "protection configuration.", CounterType.WARN); // is this really a
2115 * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
2116 * "Number of times a new device was learned",
2117 * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
2118 * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
2119 * "Number of times a packetIn was received on an internal port " +
2120 * "for a known device.", CounterType.ALWAYS_COUNT);
2121 * debugCounters.registerCounter(CNT_NEW_ENTITY,
2122 * "Number of times a new entity was learned for an existing device",
2123 * CounterType.ALWAYS_COUNT);
2124 * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
2125 * "Number of times device properties have changed",
2126 * CounterType.ALWAYS_COUNT);
2127 * debugCounters.registerCounter(CNT_DEVICE_MOVED,
2128 * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
2129 * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
2130 * "Number of times the entity cleanup task has been run",
2131 * CounterType.ALWAYS_COUNT);
2132 * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
2133 * "Number of times entities have been removed due to timeout " +
2134 * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
2135 * CounterType.ALWAYS_COUNT);
2136 * debugCounters.registerCounter(CNT_DEVICE_DELETED,
2137 * "Number of devices that have been removed due to inactivity",
2138 * CounterType.ALWAYS_COUNT);
2139 * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
2140 * "Number of devices that required reclassification and have been " +
2141 * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
2142 * debugCounters.registerCounter(CNT_DEVICE_STORED,
2143 * "Number of device entries written or updated to the sync store",
2144 * CounterType.ALWAYS_COUNT);
2145 * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
2146 * "Number of times a device update to the sync store was " +
2147 * "requested but not performed because the same device entities " +
2148 * "have recently been updated already", CounterType.ALWAYS_COUNT);
2149 * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
2150 * "Number of devices that were removed from the sync store " +
2151 * "because the local controller removed the device due to " +
2152 * "inactivity", CounterType.ALWAYS_COUNT);
2153 * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
2154 * "Number of times an operation on the sync store resulted in " +
2155 * "sync exception", CounterType.WARN); // it this an error?
2156 * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
2157 * "Number of devices that were read from the sync store after " +
2158 * "the local controller transitioned from SLAVE to MASTER",
2159 * CounterType.ALWAYS_COUNT);
2160 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
2161 * "Number of times the task to consolidate entries in the " +
2162 * "store witch live known devices has been run",
2163 * CounterType.ALWAYS_COUNT);
2164 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
2165 * "Number of times a device has been removed from the sync " +
2166 * "store because no corresponding live device is known. " +
2167 * "This indicates a remote controller still writing device " +
2168 * "entries despite the local controller being MASTER or an " +
2169 * "incosistent store update from the local controller.",
2170 * CounterType.WARN);
2171 * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
2172 * "Number of times this controller has transitioned from SLAVE " +
2173 * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
2178 public HostNodeConnector hostFind(InetAddress networkAddress) {
2179 // TODO Auto-generated method stub
2184 public HostNodeConnector hostQuery(InetAddress networkAddress) {
2185 // TODO Auto-generated method stub
2190 public Future<HostNodeConnector> discoverHost(InetAddress networkAddress) {
2191 // TODO Auto-generated method stub
2196 public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress) {
2197 // TODO Auto-generated method stub
2202 public Set<HostNodeConnector> getAllHosts() {
2203 Collection<Device> devices = Collections
2204 .unmodifiableCollection(deviceMap.values());
2205 Iterator<Device> i = devices.iterator();
2206 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2207 while (i.hasNext()) {
2208 Device device = i.next();
2209 nc.add(device.toHostNodeConnector());
2215 public Set<HostNodeConnector> getActiveStaticHosts() {
2216 Collection<Device> devices = Collections
2217 .unmodifiableCollection(deviceMap.values());
2218 Iterator<Device> i = devices.iterator();
2219 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2220 while (i.hasNext()) {
2221 Device device = i.next();
2222 if (device.isStaticHost())
2223 nc.add(device.toHostNodeConnector());
2229 public Set<HostNodeConnector> getInactiveStaticHosts() {
2230 // TODO Auto-generated method stub
2235 public Status addStaticHost(String networkAddress, String dataLayerAddress,
2236 NodeConnector nc, String vlan) {
2237 Long mac = HexEncode.stringToLong(dataLayerAddress);
2239 InetAddress addr = InetAddress.getByName(networkAddress);
2240 int ip = toIPv4Address(addr.getAddress());
2241 Entity e = new Entity(mac, Short.valueOf(vlan), ip, nc, new Date());
2243 if (switchManager.isNodeConnectorEnabled(e.getPort())) {
2244 Device d = this.learnDeviceByEntity(e);
2245 d.setStaticHost(true);
2248 "Switch or switchport is not up, adding host {} to inactive list",
2249 addr.getHostName());
2250 inactiveStaticDevices.put(e.getPort(), e);
2252 return new Status(StatusCode.SUCCESS);
2253 } catch (UnknownHostException e) {
2254 return new Status(StatusCode.INTERNALERROR);
2259 public Status removeStaticHost(String networkAddress) {
2262 addr = toIPv4Address(InetAddress.getByName(networkAddress)
2264 } catch (UnknownHostException e) {
2265 return new Status(StatusCode.NOTFOUND, "Host does not exist");
2267 Iterator<Device> di = this.getDeviceIteratorForQuery(null, null, addr,
2269 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2270 while (di.hasNext()) {
2271 Device d = di.next();
2272 if (d.isStaticHost()) {
2274 for (IfNewHostNotify notify : newHostNotify)
2275 notify.notifyHTClientHostRemoved(d.toHostNodeConnector());
2276 for (IDeviceListener listener : listeners)
2277 listener.deviceRemoved(d);
2280 return new Status(StatusCode.SUCCESS);
2284 public void notifyNode(Node node, UpdateType type,
2285 Map<String, Property> propMap) {
2288 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2291 logger.debug("Received removed node {}", node);
2292 for (Entry<Long, Device> d : deviceMap.entrySet()) {
2293 Device device = d.getValue();
2294 HostNodeConnector host = device.toHostNodeConnector();
2295 if (host.getnodeconnectorNode().equals(node)) {
2296 logger.debug("Node: {} is down, remove from Hosts_DB", node);
2297 deleteDevice(device);
2298 for (IfNewHostNotify notify : newHostNotify)
2299 notify.notifyHTClientHostRemoved(host);
2300 for (IDeviceListener listener : listeners)
2301 listener.deviceRemoved(device);
2311 public void notifyNodeConnector(NodeConnector nodeConnector,
2312 UpdateType type, Map<String, Property> propMap) {
2313 if (nodeConnector == null)
2315 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2324 State state = (State) propMap.get(State.StatePropName);
2325 if ((state != null) && (state.getValue() == State.EDGE_UP)) {
2334 logger.debug("handleNodeConnectorStatusUp {}", nodeConnector);
2336 Entity ent = inactiveStaticDevices.get(nodeConnector);
2337 Device device = this.learnDeviceByEntity(ent);
2338 HostNodeConnector host = device.toHostNodeConnector();
2340 inactiveStaticDevices.remove(nodeConnector);
2341 for (IfNewHostNotify notify : newHostNotify)
2342 notify.notifyHTClient(host);
2343 for (IDeviceListener listener : listeners)
2344 listener.deviceAdded(device);
2346 logger.debug("handleNodeConnectorStatusDown {}", nodeConnector);
2349 // remove all devices on the node that went down.
2350 for (Entry<Long, Device> entry : deviceMap.entrySet()) {
2351 Device device = entry.getValue();
2352 HostNodeConnector host = device.toHostNodeConnector();
2353 if (host.getnodeConnector().equals(nodeConnector)) {
2354 deleteDevice(device);
2355 for (IfNewHostNotify notify : newHostNotify)
2356 notify.notifyHTClientHostRemoved(host);
2357 for (IDeviceListener listener : listeners)
2358 listener.deviceRemoved(device);
2367 * For testing: consolidate the store NOW
2369 // void scheduleConsolidateStoreNow() {
2370 // this.storeConsolidateTask.reschedule(0, TimeUnit.MILLISECONDS);
2373 // private class DeviceSyncManager {
2374 // // maps (opaque) deviceKey to the time in System.nanoTime() when we
2375 // // last wrote the device to the sync store
2376 // private ConcurrentMap<Long, Long> lastWriteTimes =
2377 // new ConcurrentHashMap<Long, Long>();
2380 // * Write the given device to storage if we are MASTER.
2381 // * Use this method if the device has significantly changed (e.g.,
2382 // * new AP, new IP, entities removed).
2383 // * @param d the device to store
2385 // public void storeDevice(Device d) {
2390 // long now = System.nanoTime();
2391 // writeUpdatedDeviceToStorage(d);
2392 // lastWriteTimes.put(d.getDeviceKey(), now);
2396 // * Write the given device to storage if we are MASTER and if the
2397 // * last write for the device was more than this.syncStoreIntervalNs
2399 // * Use this method to updated last active times in the store.
2400 // * @param d the device to store
2402 // public void storeDeviceThrottled(Device d) {
2403 // long intervalNs = syncStoreWriteIntervalMs*1000L*1000L;
2408 // long now = System.nanoTime();
2409 // Long last = lastWriteTimes.get(d.getDeviceKey());
2410 // if (last == null ||
2411 // now - last > intervalNs) {
2412 // writeUpdatedDeviceToStorage(d);
2413 // lastWriteTimes.put(d.getDeviceKey(), now);
2415 // debugCounters.updateCounter(CNT_DEVICE_STORE_THROTTLED);
2420 // * Remove the given device from the store. If only some entities have
2421 // * been removed the updated device should be written using
2422 // * {@link #storeDevice(Device)}
2425 // public void removeDevice(Device d) {
2428 // // FIXME: could we have a problem with concurrent put to the
2429 // // hashMap? I.e., we write a stale entry to the map after the
2430 // // delete and now are left with an entry we'll never clean up
2431 // lastWriteTimes.remove(d.getDeviceKey());
2433 // // TODO: should probably do versioned delete. OTOH, even
2434 // // if we accidentally delete, we'll write it again after
2435 // // the next entity ....
2436 // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2437 // storeClient.delete(DeviceSyncRepresentation.computeKey(d));