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.util.ArrayList;
42 import java.util.Calendar;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.Comparator;
46 import java.util.Date;
47 import java.util.EnumSet;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.LinkedList;
52 import java.util.List;
54 import java.util.Queue;
56 import java.util.concurrent.ConcurrentHashMap;
57 import java.util.concurrent.Executors;
58 import java.util.concurrent.Future;
59 import java.util.concurrent.ScheduledExecutorService;
60 import java.util.concurrent.TimeUnit;
62 import org.opendaylight.controller.hosttracker.Entity;
63 import org.opendaylight.controller.hosttracker.IDevice;
64 import org.opendaylight.controller.hosttracker.IDeviceListener;
65 import org.opendaylight.controller.hosttracker.IDeviceService;
66 import org.opendaylight.controller.hosttracker.IEntityClass;
67 import org.opendaylight.controller.hosttracker.IEntityClassListener;
68 import org.opendaylight.controller.hosttracker.IEntityClassifierService;
69 import org.opendaylight.controller.hosttracker.IfIptoHost;
70 import org.opendaylight.controller.hosttracker.SwitchPort;
71 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
72 import org.opendaylight.controller.sal.core.Edge;
73 import org.opendaylight.controller.sal.core.NodeConnector;
74 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
75 import org.opendaylight.controller.sal.packet.ARP;
76 import org.opendaylight.controller.sal.packet.Ethernet;
77 import org.opendaylight.controller.sal.packet.IDataPacketService;
78 import org.opendaylight.controller.sal.packet.IListenDataPacket;
79 import org.opendaylight.controller.sal.packet.Packet;
80 import org.opendaylight.controller.sal.packet.PacketResult;
81 import org.opendaylight.controller.sal.packet.RawPacket;
82 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
83 import org.opendaylight.controller.sal.utils.ListenerDispatcher;
84 import org.opendaylight.controller.sal.utils.MultiIterator;
85 import org.opendaylight.controller.sal.utils.SingletonTask;
86 import org.opendaylight.controller.sal.utils.Status;
87 import org.opendaylight.controller.switchmanager.ISwitchManager;
88 import org.opendaylight.controller.topologymanager.ITopologyManager;
89 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
94 * DeviceManager creates Devices based upon MAC addresses seen in the network.
95 * It tracks any network addresses mapped to the Device, and its location within
100 public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
101 IListenDataPacket, ITopologyManagerAware, IfIptoHost {
102 protected static Logger logger = LoggerFactory
103 .getLogger(DeviceManagerImpl.class);
105 public static final String MODULE_NAME = "devicemanager";
107 // protected ITopologyService topology;
108 // protected IStorageSourceService storageSource;
109 // protected IRestApiService restApi;
110 // protected IThreadPoolService threadPool;
111 // protected IFlowReconcileService flowReconcileMgr;
112 // protected IFlowReconcileEngineService flowReconcileEngine;
113 // protected IDebugCounterService debugCounters;
114 // private ISyncService syncService;
115 // private IStoreClient<String,DeviceSyncRepresentation> storeClient;
116 // private DeviceSyncManager deviceSyncManager;
118 private ITopologyManager topology;
119 private ISwitchManager switchManager = null;
120 private IDataPacketService dataPacketService = null;
122 public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
123 public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
124 + "-reconcileRequest";
125 public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
126 + "-reconcileNoSourceDevice";
127 public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
128 + "-reconcileNoDestDevice";
129 public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
130 + "-broadcastSource";
131 public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
132 public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
133 public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
134 + "-dhcpClientNameSnooped";
135 public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
136 + "-deviceOnInternalPortNotLearned";
137 public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
138 + "-packetNotAllowed";
139 public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
140 public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
141 + "-packetOnInternalPortForKnownDevice";
142 public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
143 public static final String CNT_DEVICE_CHANGED = MODULE_NAME
145 public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
146 public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
147 + "-cleanupEntitiesRuns";
148 public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
149 + "-entityRemovedTimeout";
150 public static final String CNT_DEVICE_DELETED = MODULE_NAME
152 public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
153 + "-deviceReclassifyDelete";
154 public static final String CNT_DEVICE_STORED = MODULE_NAME
156 public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
157 + "-deviceStoreThrottled";
158 public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
159 + "-deviceRemovedFromStore";
160 public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
162 public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
163 + "-devicesFromStore";
164 public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
165 + "-consolidateStoreRuns";
166 public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
167 + "-consolidateStoreDevicesRemoved";
169 static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
170 .getCanonicalName() + ".stateStore";
173 * Time interval between writes of entries for the same device to the sync
176 // static final int DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS =
177 // 5*60*1000; // 5 min
178 // private int syncStoreWriteIntervalMs =
179 // DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS;
182 * Time after SLAVE->MASTER until we run the consolidate store code.
184 // static final int DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS =
185 // 15*1000; // 15 sec
186 // private int initialSyncStoreConsolidateMs =
187 // DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS;
190 * Time interval between consolidate store runs.
192 // static final int DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS =
193 // 75*60*1000; // 75 min
194 // private final int syncStoreConsolidateIntervalMs =
195 // DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS;
198 * Time in milliseconds before entities will expire
200 protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
203 * Time in seconds between cleaning up old entities/devices
205 protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
208 * This is the master device map that maps device IDs to {@link Device}
211 protected ConcurrentHashMap<Long, Device> deviceMap;
214 * Counter used to generate device keys
216 protected long deviceKeyCounter = 0;
219 * Lock for incrementing the device key counter
221 protected Object deviceKeyLock = new Object();
224 * This is the primary entity index that contains all entities
226 protected DeviceUniqueIndex primaryIndex;
229 * This stores secondary indices over the fields in the devices
231 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
234 * This map contains state for each of the {@ref IEntityClass} that exist
236 protected ConcurrentHashMap<String, ClassState> classStateMap;
239 * This is the list of indices we want on a per-class basis
241 protected Set<EnumSet<DeviceField>> perClassIndices;
244 * The entity classifier currently in use
246 protected IEntityClassifierService entityClassifier;
249 * Used to cache state about specific entity classes
251 protected class ClassState {
256 protected DeviceUniqueIndex classIndex;
259 * This stores secondary indices over the fields in the device for the
262 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
265 * Allocate a new {@link ClassState} object for the class
268 * the class to use for the state
270 public ClassState(IEntityClass clazz) {
271 EnumSet<DeviceField> keyFields = clazz.getKeyFields();
272 EnumSet<DeviceField> primaryKeyFields = entityClassifier
274 boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
276 if (!keyFieldsMatchPrimary)
277 classIndex = new DeviceUniqueIndex(keyFields);
279 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
280 for (EnumSet<DeviceField> fields : perClassIndices) {
281 secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
287 * Device manager event listeners reclassifyDeviceListeners are notified
288 * first before reconcileDeviceListeners. This is to make sure devices are
289 * correctly reclassified before reconciliation.
291 protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
294 * A device update event to be dispatched
296 protected static class DeviceUpdate {
302 * The affected device
304 protected Device device;
307 * The change that was made
309 protected Change change;
312 * If not added, then this is the list of fields changed
314 protected EnumSet<DeviceField> fieldsChanged;
316 public DeviceUpdate(Device device, Change change,
317 EnumSet<DeviceField> fieldsChanged) {
319 this.device = device;
320 this.change = change;
321 this.fieldsChanged = fieldsChanged;
325 public String toString() {
326 String devIdStr = device.getEntityClass().getName() + "::"
327 + device.getMACAddressString();
328 return "DeviceUpdate [device=" + devIdStr + ", change=" + change
329 + ", fieldsChanged=" + fieldsChanged + "]";
335 * AttachmentPointComparator
337 * Compares two attachment points and returns the latest one. It is assumed
338 * that the two attachment points are in the same L2 domain.
342 protected class AttachmentPointComparator implements
343 Comparator<AttachmentPoint> {
344 public AttachmentPointComparator() {
349 public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
350 // First compare based on L2 domain ID;
352 // XXX - missing functionality -- need topology
353 // long oldDomain = topology.getL2DomainId(oldSw);
354 // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
356 boolean oldBD = false;
358 // XXX - missing functionality -- need topology
359 // long newDomain = topology.getL2DomainId(newSw);
360 // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
362 boolean newBD = false;
364 if (oldDomain < newDomain)
366 else if (oldDomain > newDomain)
369 // Give preference to OFPP_LOCAL always
370 if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
371 && newAP.getPort().getType()
372 .equals(NodeConnectorIDType.SWSTACK)) {
374 } else if (oldAP.getPort().getType()
375 .equals(NodeConnectorIDType.SWSTACK)
376 && !newAP.getPort().getType()
377 .equals(NodeConnectorIDType.SWSTACK)) {
381 // We expect that the last seen of the new AP is higher than
382 // old AP, if it is not, just reverse and send the negative
384 if (oldAP.getActiveSince() > newAP.getActiveSince())
385 return -compare(newAP, oldAP);
387 long activeOffset = 0;
388 // XXX - missing functionality -- need topology
389 // if (!topology.isConsistent(oldSw, oldPort, newSw, newPort)) {
390 if (!newBD && oldBD) {
393 if (newBD && oldBD) {
394 activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
395 } else if (newBD && !oldBD) {
396 activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
400 // // The attachment point is consistent.
401 // activeOffset = AttachmentPoint.CONSISTENT_TIMEOUT;
404 if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
405 || (newAP.getLastSeen() > oldAP.getLastSeen()
406 + AttachmentPoint.INACTIVITY_INTERVAL)) {
414 * Comparator for sorting by cluster ID
416 public AttachmentPointComparator apComparator;
419 * Switch ports where attachment points shouldn't be learned
421 private Set<SwitchPort> suppressAPs;
424 * Periodic task to clean up expired entities
426 public SingletonTask entityCleanupTask;
428 // ********************
429 // Dependency injection
430 // ********************
432 void setDataPacketService(IDataPacketService s) {
433 this.dataPacketService = s;
436 void unsetDataPacketService(IDataPacketService s) {
437 if (this.dataPacketService == s) {
438 this.dataPacketService = null;
442 public void setTopologyManager(ITopologyManager s) {
446 public void unsetTopologyManager(ITopologyManager s) {
447 if (this.topology == s) {
448 logger.debug("Topology Manager Service removed!");
449 this.topology = null;
453 private volatile boolean stopped = true;
454 private ScheduledExecutorService ses;
462 public void start() {
463 this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
465 // XXX - TODO need to make it possible to register a non-default
467 entityClassifier = new DefaultEntityClassifier();
468 this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
469 this.suppressAPs = Collections
470 .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
471 primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
472 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
474 deviceMap = new ConcurrentHashMap<Long, Device>();
475 classStateMap = new ConcurrentHashMap<String, ClassState>();
476 apComparator = new AttachmentPointComparator();
478 addIndex(true, EnumSet.of(DeviceField.IPV4));
480 // floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
481 // floodlightProvider.addHAListener(this.haListenerDelegate);
482 // if (topology != null)
483 // topology.addListener(this);
484 // flowReconcileMgr.addFlowReconcileListener(this);
485 // entityClassifier.addListener(this);
488 // XXX - Should use a common threadpool but this doesn't currently exist
489 ses = Executors.newScheduledThreadPool(1);
490 Runnable ecr = new Runnable() {
495 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
499 entityCleanupTask = new SingletonTask(ses, ecr);
500 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
503 * XXX Missing functionality if (restApi != null) {
504 * restApi.addRestletRoutable(new DeviceRoutable()); } else {
505 * logger.debug("Could not instantiate REST API"); }
508 registerDeviceManagerDebugCounters();
511 * XXX Missing functionality try {
512 * this.syncService.registerStore(DEVICE_SYNC_STORE_NAME, Scope.LOCAL);
513 * this.storeClient = this.syncService
514 * .getStoreClient(DEVICE_SYNC_STORE_NAME, String.class,
515 * DeviceSyncRepresentation.class); } catch (SyncException e) { throw
516 * new FloodlightModuleException("Error while setting up sync service",
519 * Runnable consolidateStoreRunner = new Runnable() {
521 * @Override public void run() { deviceSyncManager.consolidateStore();
522 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
523 * TimeUnit.MILLISECONDS); debugCounters.flushCounters(); } };
524 * storeConsolidateTask = new SingletonTask(ses,
525 * consolidateStoreRunner); if (isMaster)
526 * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
527 * TimeUnit.MILLISECONDS);
532 * Periodic task to consolidate entries in the store. I.e., delete entries
533 * in the store that are not known to DeviceManager
535 // XXX - Missing functionality
536 // private SingletonTask storeConsolidateTask;
538 // *********************
539 // IDeviceManagerService
540 // *********************
542 void setSwitchManager(ISwitchManager s) {
543 logger.debug("SwitchManager set");
544 this.switchManager = s;
547 void unsetSwitchManager(ISwitchManager s) {
548 if (this.switchManager == s) {
549 logger.debug("SwitchManager removed!");
550 this.switchManager = null;
555 public IDevice getDevice(Long deviceKey) {
556 return deviceMap.get(deviceKey);
560 public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
561 NodeConnector port) throws IllegalArgumentException {
562 if (vlan != null && vlan.shortValue() <= 0)
564 if (ipv4Address != null && ipv4Address == 0)
566 Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
567 if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
568 throw new IllegalArgumentException("Not all key fields specified."
569 + " Required fields: " + entityClassifier.getKeyFields());
571 return findDeviceByEntity(e);
575 public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
576 Short vlan, Integer ipv4Address) throws IllegalArgumentException {
577 if (vlan != null && vlan.shortValue() <= 0)
579 if (ipv4Address != null && ipv4Address == 0)
581 Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
582 if (entityClass == null
583 || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
584 throw new IllegalArgumentException("Not all key fields and/or "
585 + " no source device specified. Required fields: "
586 + entityClassifier.getKeyFields());
588 return findDestByEntity(entityClass, e);
592 public Collection<? extends IDevice> getAllDevices() {
593 return Collections.unmodifiableCollection(deviceMap.values());
597 public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
599 perClassIndices.add(keyFields);
601 secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
606 public Iterator<? extends IDevice> queryDevices(Long macAddress,
607 Short vlan, Integer ipv4Address, NodeConnector port) {
608 DeviceIndex index = null;
609 if (secondaryIndexMap.size() > 0) {
610 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
612 index = secondaryIndexMap.get(keys);
615 Iterator<Device> deviceIterator = null;
617 // Do a full table scan
618 deviceIterator = deviceMap.values().iterator();
621 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
622 vlan, ipv4Address, port, null);
623 deviceIterator = new DeviceIndexInterator(this,
624 index.queryByEntity(entity));
627 DeviceIterator di = new DeviceIterator(deviceIterator, null,
628 macAddress, vlan, ipv4Address, port);
633 public Iterator<? extends IDevice> queryClassDevices(
634 IEntityClass entityClass, Long macAddress, Short vlan,
635 Integer ipv4Address, NodeConnector port) {
636 ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
637 ClassState classState = getClassState(entityClass);
639 DeviceIndex index = null;
640 if (classState.secondaryIndexMap.size() > 0) {
641 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
643 index = classState.secondaryIndexMap.get(keys);
646 Iterator<Device> iter;
648 index = classState.classIndex;
651 return new DeviceIterator(deviceMap.values().iterator(),
652 new IEntityClass[] { entityClass }, macAddress, vlan,
655 // scan the entire class
656 iter = new DeviceIndexInterator(this, index.getAll());
660 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
661 vlan, ipv4Address, port, null);
662 iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
666 return new MultiIterator<Device>(iterators.iterator());
669 protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
670 Short vlan, Integer ipv4Address, NodeConnector port) {
671 DeviceIndex index = null;
672 if (secondaryIndexMap.size() > 0) {
673 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
675 index = secondaryIndexMap.get(keys);
678 Iterator<Device> deviceIterator = null;
680 // Do a full table scan
681 deviceIterator = deviceMap.values().iterator();
684 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
685 vlan, ipv4Address, port, null);
686 deviceIterator = new DeviceIndexInterator(this,
687 index.queryByEntity(entity));
690 DeviceIterator di = new DeviceIterator(deviceIterator, null,
691 macAddress, vlan, ipv4Address, port);
696 public void addListener(IDeviceListener listener) {
697 deviceListeners.addListener("device", listener);
702 public void addSuppressAPs(NodeConnector port) {
703 suppressAPs.add(new SwitchPort(port));
707 public void removeSuppressAPs(NodeConnector port) {
708 suppressAPs.remove(new SwitchPort(port));
712 public Set<SwitchPort> getSuppressAPs() {
713 return Collections.unmodifiableSet(suppressAPs);
716 private void logListeners() {
717 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
718 if (listeners != null) {
719 StringBuffer sb = new StringBuffer();
720 sb.append("DeviceListeners: ");
721 for (IDeviceListener l : listeners) {
722 sb.append(l.getName());
725 logger.debug(sb.toString());
730 // IFlowReconcileListener
733 * XXX - Missing functionality
735 * @Override public Command reconcileFlows(ArrayList<OFMatchReconcile>
736 * ofmRcList) { ListIterator<OFMatchReconcile> iter =
737 * ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm =
740 * // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) {
743 * if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { return
746 * protected Command reconcileFlow(OFMatchReconcile ofm) {
747 * debugCounters.updateCounter(CNT_RECONCILE_REQUEST); // Extract source
748 * entity information Entity srcEntity =
749 * getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) {
750 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
753 * // Find the device by source entity Device srcDevice =
754 * findDeviceByEntity(srcEntity); if (srcDevice == null) {
755 * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
756 * Command.STOP; } // Store the source device in the context
757 * fcStore.put(ofm.cntx, CONTEXT_SRC_DEVICE, srcDevice);
759 * // Find the device matching the destination from the entity // classes of
760 * the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid,
761 * false); Device dstDevice = null; if (dstEntity != null) { dstDevice =
762 * findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice !=
763 * null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else
764 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } else {
765 * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } if
766 * (logger.isTraceEnabled()) {
767 * logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " +
768 * "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
769 * srcEntity, srcDevice, dstEntity, dstDevice } ); } return
770 * Command.CONTINUE; }
778 public PacketResult receiveDataPacket(RawPacket inPkt) {
779 // XXX - Can this really pass in null? Why would you ever want that?
781 return PacketResult.IGNORED;
784 throw new Exception("Sample");
785 } catch (Exception e) {
786 logger.error("Sample stack trace", e);
789 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
791 if (formattedPak instanceof Ethernet) {
792 eth = (Ethernet) formattedPak;
794 return PacketResult.IGNORED;
797 // Extract source entity information
798 NodeConnector inPort = inPkt.getIncomingNodeConnector();
799 Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
800 if (srcEntity == null) {
801 // debugCounters.updateCounter(CNT_BROADCAST_SOURCE);
802 return PacketResult.CONSUME;
805 // Learn from ARP packet for special VRRP settings.
806 // In VRRP settings, the source MAC address and sender MAC
807 // addresses can be different. In such cases, we need to learn
808 // the IP to MAC mapping of the VRRP IP address. The source
809 // entity will not have that information. Hence, a separate call
810 // to learn devices in such cases.
811 learnDeviceFromArpResponseData(eth, inPort);
813 // Learn/lookup device information
814 Device srcDevice = learnDeviceByEntity(srcEntity);
815 if (srcDevice == null) {
816 // debugCounters.updateCounter(CNT_NO_SOURCE);
817 return PacketResult.CONSUME;
819 logger.trace("Saw packet from device {}", srcDevice);
821 // // Store the source device in the context
822 // fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice);
824 // // Find the device matching the destination from the entity
825 // // classes of the source.
826 // Entity dstEntity = getDestEntityFromPacket(eth);
827 // Device dstDevice = null;
828 // if (dstEntity != null) {
830 // findDestByEntity(srcDevice.getEntityClass(), dstEntity);
831 // if (dstDevice != null)
832 // fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice);
834 // //debugCounters.updateCounter(CNT_NO_DEST);
836 // //debugCounters.updateCounter(CNT_NO_DEST);
839 // if (logger.isTraceEnabled()) {
840 // logger.trace("Received PI: {} on switch {}, port {} *** eth={}" +
841 // " *** srcDev={} *** dstDev={} *** ",
842 // new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
843 // srcDevice, dstDevice });
846 // snoopDHCPClientName(eth, srcDevice);
848 return PacketResult.KEEP_PROCESSING;
856 * Snoop and record client-provided host name from DHCP requests
861 // private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
862 // if (! (eth.getPayload() instanceof IPv4) )
864 // IPv4 ipv4 = (IPv4) eth.getPayload();
865 // if (! (ipv4.getPayload() instanceof UDP) )
867 // UDP udp = (UDP) ipv4.getPayload();
868 // if (!(udp.getPayload() instanceof DHCP))
870 // DHCP dhcp = (DHCP) udp.getPayload();
871 // byte opcode = dhcp.getOpCode();
872 // if (opcode == DHCP.OPCODE_REQUEST) {
873 // DHCPOption dhcpOption = dhcp.getOption(
874 // DHCPOptionCode.OptionCode_Hostname);
875 // if (dhcpOption != null) {
876 // debugCounters.updateCounter(CNT_DHCP_CLIENT_NAME_SNOOPED);
877 // srcDevice.dhcpClientName = new String(dhcpOption.getData());
883 * Check whether the given attachment point is valid given the current
890 * @return true if it's a valid attachment point
892 public boolean isValidAttachmentPoint(NodeConnector port) {
893 // XXX - missing functionality -- need topology module
894 // if (topology.isAttachmentPointPort(port) == false)
896 if (topology.isInternal(port))
898 if (!switchManager.isNodeConnectorEnabled(port))
900 if (suppressAPs.contains(new SwitchPort(port)))
907 * Get sender IP address from packet if the packet is either an ARP packet.
913 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
914 if (eth.getPayload() instanceof ARP) {
915 ARP arp = (ARP) eth.getPayload();
916 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
917 && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
918 return toIPv4Address(arp.getSenderProtocolAddress());
925 * Parse an entity from an {@link Ethernet} packet.
928 * the packet to parse
930 * the switch on which the packet arrived
932 * the original packetin
933 * @return the entity from the packet
935 protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
936 byte[] dlAddrArr = eth.getSourceMACAddress();
937 long dlAddr = toLong(dlAddrArr);
939 // Ignore broadcast/multicast source
940 if ((dlAddrArr[0] & 0x1) != 0)
943 // XXX missing functionality
945 int nwSrc = getSrcNwAddr(eth, dlAddr);
946 return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
951 * Learn device from ARP data in scenarios where the Ethernet source MAC is
952 * different from the sender hardware address in ARP data.
954 protected void learnDeviceFromArpResponseData(Ethernet eth,
955 NodeConnector port) {
957 if (!(eth.getPayload() instanceof ARP))
959 ARP arp = (ARP) eth.getPayload();
961 byte[] dlAddrArr = eth.getSourceMACAddress();
962 long dlAddr = toLong(dlAddrArr);
964 byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
965 long senderAddr = toLong(senderHardwareAddr);
967 if (dlAddr == senderAddr)
970 // Ignore broadcast/multicast source
971 if ((senderHardwareAddr[0] & 0x1) != 0)
974 // short vlan = eth.getVlanID();
975 int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
977 Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
980 learnDeviceByEntity(e);
984 * Get a (partial) entity for the destination from the packet.
989 // protected Entity getDestEntityFromPacket(Ethernet eth) {
990 // byte[] dlAddrArr = eth.getDestinationMACAddress();
991 // long dlAddr = Ethernet.toLong(dlAddrArr);
992 // short vlan = eth.getVlanID();
995 // // Ignore broadcast/multicast destination
996 // if ((dlAddrArr[0] & 0x1) != 0)
999 // if (eth.getPayload() instanceof IPv4) {
1000 // IPv4 ipv4 = (IPv4) eth.getPayload();
1001 // nwDst = ipv4.getDestinationAddress();
1004 // return new Entity(dlAddr,
1005 // ((vlan >= 0) ? vlan : null),
1006 // ((nwDst != 0) ? nwDst : null),
1013 * Parse an entity from an OFMatchWithSwDpid.
1015 * @param ofmWithSwDpid
1016 * @return the entity from the packet
1018 // private Entity getEntityFromFlowMod(OFMatchWithSwDpid ofmWithSwDpid,
1019 // boolean isSource) {
1020 // byte[] dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerSource();
1021 // int nwSrc = ofmWithSwDpid.getOfMatch().getNetworkSource();
1023 // dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerDestination();
1024 // nwSrc = ofmWithSwDpid.getOfMatch().getNetworkDestination();
1027 // long dlAddr = Ethernet.toLong(dlAddrArr);
1029 // // Ignore broadcast/multicast source
1030 // if ((dlAddrArr[0] & 0x1) != 0)
1033 // Long swDpid = null;
1034 // Short inPort = null;
1037 // swDpid = ofmWithSwDpid.getSwitchDataPathId();
1038 // inPort = ofmWithSwDpid.getOfMatch().getInputPort();
1041 // /**for the new flow cache design, the flow mods retrived are not always
1042 // from the source, learn AP should be disabled --meiyang*/
1043 // boolean learnap = false;
1045 // * if (swDpid == null ||
1046 // inPort == null ||
1047 // !isValidAttachmentPoint(swDpid, inPort)) {
1048 // // If this is an internal port or we otherwise don't want
1049 // // to learn on these ports. In the future, we should
1050 // // handle this case by labeling flows with something that
1051 // // will give us the entity class. For now, we'll do our
1052 // // best assuming attachment point information isn't used
1053 // // as a key field.
1058 // short vlan = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLan();
1059 // return new Entity(dlAddr,
1060 // ((vlan >= 0) ? vlan : null),
1061 // ((nwSrc != 0) ? nwSrc : null),
1062 // (learnap ? swDpid : null),
1063 // (learnap ? (int)inPort : null),
1068 * Look up a {@link Device} based on the provided {@link Entity}. We first
1069 * check the primary index. If we do not find an entry there we classify the
1070 * device into its IEntityClass and query the classIndex. This implies that
1071 * all key field of the current IEntityClassifier must be present in the
1072 * entity for the lookup to succeed!
1075 * the entity to search for
1076 * @return The {@link Device} object if found
1078 protected Device findDeviceByEntity(Entity entity) {
1079 // Look up the fully-qualified entity to see if it already
1080 // exists in the primary entity index.
1081 Long deviceKey = primaryIndex.findByEntity(entity);
1082 IEntityClass entityClass = null;
1084 if (deviceKey == null) {
1085 // If the entity does not exist in the primary entity index,
1086 // use the entity classifier for find the classes for the
1087 // entity. Look up the entity in the returned class'
1088 // class entity index.
1089 entityClass = entityClassifier.classifyEntity(entity);
1090 if (entityClass == null) {
1093 ClassState classState = getClassState(entityClass);
1095 if (classState.classIndex != null) {
1096 deviceKey = classState.classIndex.findByEntity(entity);
1099 if (deviceKey == null)
1101 return deviceMap.get(deviceKey);
1105 * Get a destination device using entity fields that corresponds with the
1106 * given source device. The source device is important since there could be
1107 * ambiguity in the destination device without the attachment point
1111 * the source device's entity class. The returned destination
1112 * will be in the same entity class as the source.
1114 * the entity to look up
1115 * @return an {@link Device} or null if no device is found.
1117 protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
1119 // Look up the fully-qualified entity to see if it
1120 // exists in the primary entity index
1121 Long deviceKey = primaryIndex.findByEntity(dstEntity);
1123 if (deviceKey == null) {
1124 // This could happen because:
1125 // 1) no destination known, or a broadcast destination
1126 // 2) if we have attachment point key fields since
1127 // attachment point information isn't available for
1128 // destination devices.
1129 // For the second case, we'll need to match up the
1130 // destination device with the class of the source
1132 ClassState classState = getClassState(reference);
1133 if (classState.classIndex == null) {
1136 deviceKey = classState.classIndex.findByEntity(dstEntity);
1138 if (deviceKey == null)
1140 return deviceMap.get(deviceKey);
1144 * Look up a {@link Device} within a particular entity class based on the
1145 * provided {@link Entity}.
1148 * the entity class to search for the entity
1150 * the entity to search for
1151 * @return The {@link Device} object if found private Device
1152 * findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
1153 * XXX - TODO throw new UnsupportedOperationException(); }
1157 * Look up a {@link Device} based on the provided {@link Entity}. Also
1158 * learns based on the new entity, and will update existing devices as
1162 * the {@link Entity}
1163 * @return The {@link Device} object if found
1165 protected Device learnDeviceByEntity(Entity entity) {
1166 logger.info("Primary index {}", primaryIndex);
1167 ArrayList<Long> deleteQueue = null;
1168 LinkedList<DeviceUpdate> deviceUpdates = null;
1169 Device device = null;
1171 // we may need to restart the learning process if we detect
1172 // concurrent modification. Note that we ensure that at least
1173 // one thread should always succeed so we don't get into infinite
1176 deviceUpdates = null;
1178 // Look up the fully-qualified entity to see if it already
1179 // exists in the primary entity index.
1180 Long deviceKey = primaryIndex.findByEntity(entity);
1181 IEntityClass entityClass = null;
1183 if (deviceKey == null) {
1184 // If the entity does not exist in the primary entity index,
1185 // use the entity classifier for find the classes for the
1186 // entity. Look up the entity in the returned class'
1187 // class entity index.
1188 entityClass = entityClassifier.classifyEntity(entity);
1189 if (entityClass == null) {
1190 // could not classify entity. No device
1194 ClassState classState = getClassState(entityClass);
1196 if (classState.classIndex != null) {
1197 deviceKey = classState.classIndex.findByEntity(entity);
1200 if (deviceKey != null) {
1201 // If the primary or secondary index contains the entity
1202 // use resulting device key to look up the device in the
1203 // device map, and use the referenced Device below.
1204 device = deviceMap.get(deviceKey);
1205 if (device == null) {
1206 // This can happen due to concurrent modification
1207 if (logger.isDebugEnabled()) {
1208 logger.debug("No device for deviceKey {} while "
1209 + "while processing entity {}", deviceKey,
1212 // if so, then try again till we don't even get the device
1214 // and so we recreate the device
1218 // If the secondary index does not contain the entity,
1219 // create a new Device object containing the entity, and
1220 // generate a new device ID if the the entity is on an
1221 // attachment point port. Otherwise ignore.
1222 if (entity.hasSwitchPort()
1223 && !isValidAttachmentPoint(entity.getPort())) {
1224 // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
1225 if (logger.isDebugEnabled()) {
1226 logger.debug("Not learning new device on internal"
1227 + " link: {}", entity);
1232 // Before we create the new device also check if
1233 // the entity is allowed (e.g., for spoofing protection)
1234 if (!isEntityAllowed(entity, entityClass)) {
1235 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1236 if (logger.isDebugEnabled()) {
1237 logger.debug("PacketIn is not allowed {} {}",
1238 entityClass.getName(), entity);
1243 synchronized (deviceKeyLock) {
1244 deviceKey = Long.valueOf(deviceKeyCounter++);
1246 device = allocateDevice(deviceKey, entity, entityClass);
1248 // Add the new device to the primary map with a simple put
1249 deviceMap.put(deviceKey, device);
1252 if (!updateIndices(device, deviceKey)) {
1253 if (deleteQueue == null)
1254 deleteQueue = new ArrayList<Long>();
1255 deleteQueue.add(deviceKey);
1259 updateSecondaryIndices(entity, entityClass, deviceKey);
1261 // We need to count and log here. If we log earlier we could
1262 // hit a concurrent modification and restart the dev creation
1263 // and potentially count the device twice.
1264 // debugCounters.updateCounter(CNT_NEW_DEVICE);
1265 if (logger.isDebugEnabled()) {
1267 "New device created: {} deviceKey={}, entity={}",
1268 new Object[] { device, deviceKey, entity });
1270 // generate new device update
1271 deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
1272 device, ADD, null));
1276 // if it gets here, we have a pre-existing Device for this Entity
1277 if (!isEntityAllowed(entity, device.getEntityClass())) {
1278 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1279 if (logger.isDebugEnabled()) {
1280 logger.info("PacketIn is not allowed {} {}", device
1281 .getEntityClass().getName(), entity);
1285 // If this is not an attachment point port we don't learn the new
1287 // and don't update indexes. But we do allow the device to continue
1290 if (entity.hasSwitchPort()
1291 && !isValidAttachmentPoint(entity.getPort())) {
1292 // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
1295 int entityindex = -1;
1296 if ((entityindex = device.entityIndex(entity)) >= 0) {
1297 // Entity already exists
1298 // update timestamp on the found entity
1299 Date lastSeen = entity.getLastSeenTimestamp();
1300 if (lastSeen == null) {
1301 lastSeen = new Date();
1302 entity.setLastSeenTimestamp(lastSeen);
1304 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
1305 // we break the loop after checking for changes to the AP
1307 // New entity for this device
1308 // compute the insertion point for the entity.
1309 // see Arrays.binarySearch()
1310 entityindex = -(entityindex + 1);
1311 Device newDevice = allocateDevice(device, entity, entityindex);
1314 EnumSet<DeviceField> changedFields = findChangedFields(device,
1317 // update the device map with a replace call
1318 boolean res = deviceMap.replace(deviceKey, device, newDevice);
1319 // If replace returns false, restart the process from the
1320 // beginning (this implies another thread concurrently
1321 // modified this Device).
1327 if (!updateIndices(device, deviceKey)) {
1330 updateSecondaryIndices(entity, device.getEntityClass(),
1333 // We need to count here after all the possible "continue"
1334 // statements in this branch
1335 // debugCounters.updateCounter(CNT_NEW_ENTITY);
1336 if (changedFields.size() > 0) {
1337 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1338 deviceUpdates = updateUpdates(deviceUpdates,
1339 new DeviceUpdate(newDevice, CHANGE, changedFields));
1341 // we break the loop after checking for changed AP
1343 // Update attachment point (will only be hit if the device
1344 // already existed and no concurrent modification)
1345 if (entity.hasSwitchPort()) {
1346 boolean moved = device.updateAttachmentPoint(entity.getPort(),
1347 entity.getLastSeenTimestamp().getTime());
1348 // TODO: use update mechanism instead of sending the
1349 // notification directly
1351 // we count device moved events in
1352 // sendDeviceMovedNotification()
1353 sendDeviceMovedNotification(device);
1354 if (logger.isTraceEnabled()) {
1355 logger.trace("Device moved: attachment points {},"
1356 + "entities {}", device.attachmentPoints,
1360 if (logger.isTraceEnabled()) {
1361 logger.trace("Device attachment point updated: "
1362 + "attachment points {}," + "entities {}",
1363 device.attachmentPoints, device.entities);
1370 if (deleteQueue != null) {
1371 for (Long l : deleteQueue) {
1372 Device dev = deviceMap.get(l);
1373 this.deleteDevice(dev);
1377 processUpdates(deviceUpdates);
1378 // deviceSyncManager.storeDeviceThrottled(device);
1383 protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
1387 protected EnumSet<DeviceField> findChangedFields(Device device,
1389 EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
1390 DeviceField.VLAN, DeviceField.SWITCHPORT);
1392 if (newEntity.getIpv4Address() == null)
1393 changedFields.remove(DeviceField.IPV4);
1394 if (newEntity.getVlan() == null)
1395 changedFields.remove(DeviceField.VLAN);
1396 if (newEntity.getPort() == null)
1397 changedFields.remove(DeviceField.SWITCHPORT);
1399 if (changedFields.size() == 0)
1400 return changedFields;
1402 for (Entity entity : device.getEntities()) {
1403 if (newEntity.getIpv4Address() == null
1404 || (entity.getIpv4Address() != null && entity
1406 .equals(newEntity.getIpv4Address())))
1407 changedFields.remove(DeviceField.IPV4);
1408 if (newEntity.getVlan() == null
1409 || (entity.getVlan() != null && entity.getVlan().equals(
1410 newEntity.getVlan())))
1411 changedFields.remove(DeviceField.VLAN);
1412 if (newEntity.getPort() == null
1413 || (entity.getPort() != null && entity.getPort().equals(
1414 newEntity.getPort())))
1415 changedFields.remove(DeviceField.SWITCHPORT);
1418 return changedFields;
1422 * Send update notifications to listeners
1425 * the updates to process.
1427 protected void processUpdates(Queue<DeviceUpdate> updates) {
1428 if (updates == null)
1430 DeviceUpdate update = null;
1431 while (null != (update = updates.poll())) {
1432 if (logger.isTraceEnabled()) {
1433 logger.trace("Dispatching device update: {}", update);
1435 // if (update.change == DeviceUpdate.Change.DELETE)
1436 // deviceSyncManager.removeDevice(update.device);
1438 // deviceSyncManager.storeDevice(update.device);
1439 List<IDeviceListener> listeners = deviceListeners
1440 .getOrderedListeners();
1441 notifyListeners(listeners, update);
1445 protected void notifyListeners(List<IDeviceListener> listeners,
1446 DeviceUpdate update) {
1447 if (listeners == null) {
1450 for (IDeviceListener listener : listeners) {
1451 switch (update.change) {
1453 listener.deviceAdded(update.device);
1456 listener.deviceRemoved(update.device);
1459 for (DeviceField field : update.fieldsChanged) {
1462 listener.deviceIPV4AddrChanged(update.device);
1465 // listener.deviceMoved(update.device);
1468 listener.deviceVlanChanged(update.device);
1471 logger.debug("Unknown device field changed {}",
1472 update.fieldsChanged.toString());
1482 * Check if the entity e has all the keyFields set. Returns false if not
1487 * the key fields to check e against
1490 protected boolean allKeyFieldsPresent(Entity e,
1491 EnumSet<DeviceField> keyFields) {
1492 for (DeviceField f : keyFields) {
1495 // MAC address is always present
1498 if (e.getIpv4Address() == null)
1502 if (e.getPort() == null)
1506 // FIXME: vlan==null is ambiguous: it can mean: not present
1508 // if (e.vlan == null) return false;
1511 // we should never get here. unless somebody extended
1513 throw new IllegalStateException();
1519 private LinkedList<DeviceUpdate> updateUpdates(
1520 LinkedList<DeviceUpdate> list, DeviceUpdate update) {
1524 list = new LinkedList<DeviceUpdate>();
1531 * Get the secondary index for a class. Will return null if the secondary
1532 * index was created concurrently in another thread.
1535 * the class for the index
1538 private ClassState getClassState(IEntityClass clazz) {
1539 ClassState classState = classStateMap.get(clazz.getName());
1540 if (classState != null)
1543 classState = new ClassState(clazz);
1544 ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
1553 * Update both the primary and class indices for the provided device. If the
1554 * update fails because of an concurrent update, will return false.
1557 * the device to update
1559 * the device key for the device
1560 * @return true if the update succeeded, false otherwise.
1562 private boolean updateIndices(Device device, Long deviceKey) {
1563 if (!primaryIndex.updateIndex(device, deviceKey)) {
1566 IEntityClass entityClass = device.getEntityClass();
1567 ClassState classState = getClassState(entityClass);
1569 if (classState.classIndex != null) {
1570 if (!classState.classIndex.updateIndex(device, deviceKey))
1577 * Update the secondary indices for the given entity and associated entity
1581 * the entity to update
1582 * @param entityClass
1583 * the entity class for the entity
1585 * the device key to set up
1587 private void updateSecondaryIndices(Entity entity,
1588 IEntityClass entityClass, Long deviceKey) {
1589 for (DeviceIndex index : secondaryIndexMap.values()) {
1590 index.updateIndex(entity, deviceKey);
1592 ClassState state = getClassState(entityClass);
1593 for (DeviceIndex index : state.secondaryIndexMap.values()) {
1594 index.updateIndex(entity, deviceKey);
1599 * Clean up expired entities/devices
1601 protected void cleanupEntities() {
1602 // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
1604 Calendar c = Calendar.getInstance();
1605 c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
1606 Date cutoff = c.getTime();
1608 ArrayList<Entity> toRemove = new ArrayList<Entity>();
1609 ArrayList<Entity> toKeep = new ArrayList<Entity>();
1611 Iterator<Device> diter = deviceMap.values().iterator();
1612 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1614 while (diter.hasNext()) {
1615 Device d = diter.next();
1618 deviceUpdates.clear();
1621 for (Entity e : d.getEntities()) {
1622 if (e.getLastSeenTimestamp() != null
1623 && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
1624 // individual entity needs to be removed
1630 if (toRemove.size() == 0) {
1634 // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
1635 for (Entity e : toRemove) {
1636 removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
1640 if (toKeep.size() > 0) {
1641 Device newDevice = allocateDevice(d.getDeviceKey(),
1642 d.getDHCPClientName(), d.oldAPs,
1643 d.attachmentPoints, toKeep, d.getEntityClass());
1645 EnumSet<DeviceField> changedFields = EnumSet
1646 .noneOf(DeviceField.class);
1647 for (Entity e : toRemove) {
1648 changedFields.addAll(findChangedFields(newDevice, e));
1650 DeviceUpdate update = null;
1651 if (changedFields.size() > 0) {
1652 update = new DeviceUpdate(d, CHANGE, changedFields);
1655 if (!deviceMap.replace(newDevice.getDeviceKey(), d,
1657 // concurrent modification; try again
1658 // need to use device that is the map now for the next
1660 d = deviceMap.get(d.getDeviceKey());
1664 if (update != null) {
1665 // need to count after all possibly continue stmts in
1667 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1668 deviceUpdates.add(update);
1671 DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
1672 if (!deviceMap.remove(d.getDeviceKey(), d)) {
1673 // concurrent modification; try again
1674 // need to use device that is the map now for the next
1676 d = deviceMap.get(d.getDeviceKey());
1679 // debugCounters.updateCounter(CNT_DEVICE_DELETED);
1681 deviceUpdates.add(update);
1683 processUpdates(deviceUpdates);
1689 protected void removeEntity(Entity removed, IEntityClass entityClass,
1690 Long deviceKey, Collection<Entity> others) {
1691 // Don't count in this method. This method CAN BE called to clean-up
1692 // after concurrent device adds/updates and thus counting here
1694 for (DeviceIndex index : secondaryIndexMap.values()) {
1695 index.removeEntityIfNeeded(removed, deviceKey, others);
1697 ClassState classState = getClassState(entityClass);
1698 for (DeviceIndex index : classState.secondaryIndexMap.values()) {
1699 index.removeEntityIfNeeded(removed, deviceKey, others);
1702 primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
1704 if (classState.classIndex != null) {
1705 classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
1711 * method to delete a given device, remove all entities first and then
1712 * finally delete the device itself.
1716 protected void deleteDevice(Device device) {
1717 // Don't count in this method. This method CAN BE called to clean-up
1718 // after concurrent device adds/updates and thus counting here
1720 ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
1721 for (Entity entity : device.getEntities()) {
1722 this.removeEntity(entity, device.getEntityClass(),
1723 device.getDeviceKey(), emptyToKeep);
1725 if (!deviceMap.remove(device.getDeviceKey(), device)) {
1726 if (logger.isDebugEnabled())
1727 logger.debug("device map does not have this device -"
1728 + device.toString());
1732 private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
1733 Integer ipv4Address, NodeConnector port) {
1734 // FIXME: vlan==null is a valid search. Need to handle this
1735 // case correctly. Note that the code will still work correctly.
1736 // But we might do a full device search instead of using an index.
1737 EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
1738 if (macAddress != null)
1739 keys.add(DeviceField.MAC);
1741 keys.add(DeviceField.VLAN);
1742 if (ipv4Address != null)
1743 keys.add(DeviceField.IPV4);
1745 keys.add(DeviceField.SWITCHPORT);
1749 protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
1750 EnumSet<DeviceField> keyFields, Entity entity) {
1751 ClassState classState = getClassState(clazz);
1752 DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
1754 return Collections.<Device> emptySet().iterator();
1755 return new DeviceIndexInterator(this, index.queryByEntity(entity));
1758 protected Device allocateDevice(Long deviceKey, Entity entity,
1759 IEntityClass entityClass) {
1760 return new Device(this, deviceKey, entity, entityClass);
1764 protected Device allocateDevice(Long deviceKey, String dhcpClientName,
1765 List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
1766 Collection<Entity> entities, IEntityClass entityClass) {
1767 return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
1768 entities, entityClass);
1771 protected Device allocateDevice(Device device, Entity entity,
1772 int insertionpoint) {
1773 return new Device(device, entity, insertionpoint);
1777 protected Device allocateDevice(Device device, Set<Entity> entities) {
1778 List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
1779 List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
1780 for (Entity entity : entities) {
1781 if (entity.getPort() != null) {
1782 AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
1783 newPossibleAPs.add(aP);
1786 if (device.attachmentPoints != null) {
1787 for (AttachmentPoint oldAP : device.attachmentPoints) {
1788 if (newPossibleAPs.contains(oldAP)) {
1793 if (newAPs.isEmpty())
1795 Device d = new Device(this, device.getDeviceKey(),
1796 device.getDHCPClientName(), newAPs, null, entities,
1797 device.getEntityClass());
1798 d.updateAttachmentPoint();
1802 // *********************
1803 // ITopologyManagerAware
1804 // *********************
1807 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
1808 Iterator<Device> diter = deviceMap.values().iterator();
1810 while (diter.hasNext()) {
1811 Device d = diter.next();
1812 if (d.updateAttachmentPoint()) {
1813 if (logger.isDebugEnabled()) {
1814 logger.debug("Attachment point changed for device: {}", d);
1816 sendDeviceMovedNotification(d);
1822 public void edgeOverUtilized(Edge edge) {
1827 public void edgeUtilBackToNormal(Edge edge) {
1831 // *********************
1832 // IEntityClassListener
1833 // *********************
1836 public void entityClassChanged(Set<String> entityClassNames) {
1838 * iterate through the devices, reclassify the devices that belong to
1839 * these entity class names
1841 Iterator<Device> diter = deviceMap.values().iterator();
1842 while (diter.hasNext()) {
1843 Device d = diter.next();
1844 if (d.getEntityClass() == null
1845 || entityClassNames.contains(d.getEntityClass().getName()))
1846 reclassifyDevice(d);
1854 * Send update notifications to listeners
1857 * the updates to process.
1859 protected void sendDeviceMovedNotification(Device d) {
1860 // debugCounters.updateCounter(CNT_DEVICE_MOVED);
1861 // deviceSyncManager.storeDevice(d);
1862 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1863 if (listeners != null) {
1864 for (IDeviceListener listener : listeners) {
1865 listener.deviceMoved(d);
1871 * this method will reclassify and reconcile a device - possibilities are -
1872 * create new device(s), remove entities from this device. If the device
1873 * entity class did not change then it returns false else true.
1877 protected boolean reclassifyDevice(Device device) {
1878 // first classify all entities of this device
1879 if (device == null) {
1880 logger.debug("In reclassify for null device");
1883 boolean needToReclassify = false;
1884 for (Entity entity : device.entities) {
1885 IEntityClass entityClass = this.entityClassifier
1886 .classifyEntity(entity);
1887 if (entityClass == null || device.getEntityClass() == null) {
1888 needToReclassify = true;
1891 if (!entityClass.getName()
1892 .equals(device.getEntityClass().getName())) {
1893 needToReclassify = true;
1897 if (needToReclassify == false) {
1901 // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
1902 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1903 // delete this device and then re-learn all the entities
1904 this.deleteDevice(device);
1905 deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
1907 if (!deviceUpdates.isEmpty())
1908 processUpdates(deviceUpdates);
1909 for (Entity entity : device.entities) {
1910 this.learnDeviceByEntity(entity);
1916 * For testing: sets the interval between writes of the same device to the
1921 // void setSyncStoreWriteInterval(int intervalMs) {
1922 // this.syncStoreWriteIntervalMs = intervalMs;
1926 * For testing: sets the time between transition to MASTER and consolidate
1931 // void setInitialSyncStoreConsolidateMs(int intervalMs) {
1932 // this.initialSyncStoreConsolidateMs = intervalMs;
1935 private long toLong(byte[] address) {
1937 for (int i = 0; i < 6; i++) {
1938 long t = (address[i] & 0xffL) << ((5 - i) * 8);
1945 * Accepts an IPv4 address in a byte array and returns the corresponding
1946 * 32-bit integer value.
1951 private static int toIPv4Address(byte[] ipAddress) {
1953 for (int i = 0; i < 4; i++) {
1954 int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
1960 private void registerDeviceManagerDebugCounters() {
1962 * XXX Missing functionality if (debugCounters == null) {
1963 * logger.error("Debug Counter Service not found."); debugCounters = new
1964 * NullDebugCounter(); return; }
1965 * debugCounters.registerCounter(CNT_INCOMING,
1966 * "All incoming packets seen by this module",
1967 * CounterType.ALWAYS_COUNT);
1968 * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
1969 * "Number of flows that have been received for reconciliation by " +
1970 * "this module", CounterType.ALWAYS_COUNT);
1971 * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
1972 * "Number of flow reconcile events that failed because no source " +
1973 * "device could be identified", CounterType.WARN); // is this really a
1974 * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
1975 * "Number of flow reconcile events that failed because no " +
1976 * "destination device could be identified", CounterType.WARN); // is
1977 * this really a warning
1978 * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
1979 * "Number of packetIns that were discarded because the source " +
1980 * "MAC was broadcast or multicast", CounterType.WARN);
1981 * debugCounters.registerCounter(CNT_NO_SOURCE,
1982 * "Number of packetIns that were discarded because the " +
1983 * "could not identify a source device. This can happen if a " +
1984 * "packet is not allowed, appears on an illegal port, does not " +
1985 * "have a valid address space, etc.", CounterType.WARN);
1986 * debugCounters.registerCounter(CNT_NO_DEST,
1987 * "Number of packetIns that did not have an associated " +
1988 * "destination device. E.g., because the destination MAC is " +
1989 * "broadcast/multicast or is not yet known to the controller.",
1990 * CounterType.ALWAYS_COUNT);
1991 * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
1992 * "Number of times a DHCP client name was snooped from a " +
1993 * "packetIn.", CounterType.ALWAYS_COUNT);
1994 * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
1995 * "Number of times packetIn was received on an internal port and" +
1996 * "no source device is known for the source MAC. The packetIn is " +
1997 * "discarded.", CounterType.WARN);
1998 * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
1999 * "Number of times a packetIn was not allowed due to spoofing " +
2000 * "protection configuration.", CounterType.WARN); // is this really a
2001 * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
2002 * "Number of times a new device was learned",
2003 * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
2004 * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
2005 * "Number of times a packetIn was received on an internal port " +
2006 * "for a known device.", CounterType.ALWAYS_COUNT);
2007 * debugCounters.registerCounter(CNT_NEW_ENTITY,
2008 * "Number of times a new entity was learned for an existing device",
2009 * CounterType.ALWAYS_COUNT);
2010 * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
2011 * "Number of times device properties have changed",
2012 * CounterType.ALWAYS_COUNT);
2013 * debugCounters.registerCounter(CNT_DEVICE_MOVED,
2014 * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
2015 * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
2016 * "Number of times the entity cleanup task has been run",
2017 * CounterType.ALWAYS_COUNT);
2018 * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
2019 * "Number of times entities have been removed due to timeout " +
2020 * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
2021 * CounterType.ALWAYS_COUNT);
2022 * debugCounters.registerCounter(CNT_DEVICE_DELETED,
2023 * "Number of devices that have been removed due to inactivity",
2024 * CounterType.ALWAYS_COUNT);
2025 * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
2026 * "Number of devices that required reclassification and have been " +
2027 * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
2028 * debugCounters.registerCounter(CNT_DEVICE_STORED,
2029 * "Number of device entries written or updated to the sync store",
2030 * CounterType.ALWAYS_COUNT);
2031 * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
2032 * "Number of times a device update to the sync store was " +
2033 * "requested but not performed because the same device entities " +
2034 * "have recently been updated already", CounterType.ALWAYS_COUNT);
2035 * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
2036 * "Number of devices that were removed from the sync store " +
2037 * "because the local controller removed the device due to " +
2038 * "inactivity", CounterType.ALWAYS_COUNT);
2039 * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
2040 * "Number of times an operation on the sync store resulted in " +
2041 * "sync exception", CounterType.WARN); // it this an error?
2042 * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
2043 * "Number of devices that were read from the sync store after " +
2044 * "the local controller transitioned from SLAVE to MASTER",
2045 * CounterType.ALWAYS_COUNT);
2046 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
2047 * "Number of times the task to consolidate entries in the " +
2048 * "store witch live known devices has been run",
2049 * CounterType.ALWAYS_COUNT);
2050 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
2051 * "Number of times a device has been removed from the sync " +
2052 * "store because no corresponding live device is known. " +
2053 * "This indicates a remote controller still writing device " +
2054 * "entries despite the local controller being MASTER or an " +
2055 * "incosistent store update from the local controller.",
2056 * CounterType.WARN);
2057 * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
2058 * "Number of times this controller has transitioned from SLAVE " +
2059 * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
2064 public HostNodeConnector hostFind(InetAddress networkAddress) {
2065 // TODO Auto-generated method stub
2070 public HostNodeConnector hostQuery(InetAddress networkAddress) {
2071 // TODO Auto-generated method stub
2076 public Future<HostNodeConnector> discoverHost(InetAddress networkAddress) {
2077 // TODO Auto-generated method stub
2082 public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress) {
2083 // TODO Auto-generated method stub
2088 public Set<HostNodeConnector> getAllHosts() {
2089 Collection<Device> devices = Collections
2090 .unmodifiableCollection(deviceMap.values());
2091 Iterator<Device> i = devices.iterator();
2092 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
2093 while (i.hasNext()) {
2094 Device device = i.next();
2095 nc.add(device.toHostNodeConnector());
2101 public Set<HostNodeConnector> getActiveStaticHosts() {
2102 // TODO Auto-generated method stub
2107 public Set<HostNodeConnector> getInactiveStaticHosts() {
2108 // TODO Auto-generated method stub
2113 public Status addStaticHost(String networkAddress, String dataLayerAddress,
2114 NodeConnector nc, String vlan) {
2115 // TODO Auto-generated method stub
2120 public Status removeStaticHost(String networkAddress) {
2121 // TODO Auto-generated method stub
2126 * For testing: consolidate the store NOW
2128 // void scheduleConsolidateStoreNow() {
2129 // this.storeConsolidateTask.reschedule(0, TimeUnit.MILLISECONDS);
2132 // private class DeviceSyncManager {
2133 // // maps (opaque) deviceKey to the time in System.nanoTime() when we
2134 // // last wrote the device to the sync store
2135 // private ConcurrentMap<Long, Long> lastWriteTimes =
2136 // new ConcurrentHashMap<Long, Long>();
2139 // * Write the given device to storage if we are MASTER.
2140 // * Use this method if the device has significantly changed (e.g.,
2141 // * new AP, new IP, entities removed).
2142 // * @param d the device to store
2144 // public void storeDevice(Device d) {
2149 // long now = System.nanoTime();
2150 // writeUpdatedDeviceToStorage(d);
2151 // lastWriteTimes.put(d.getDeviceKey(), now);
2155 // * Write the given device to storage if we are MASTER and if the
2156 // * last write for the device was more than this.syncStoreIntervalNs
2158 // * Use this method to updated last active times in the store.
2159 // * @param d the device to store
2161 // public void storeDeviceThrottled(Device d) {
2162 // long intervalNs = syncStoreWriteIntervalMs*1000L*1000L;
2167 // long now = System.nanoTime();
2168 // Long last = lastWriteTimes.get(d.getDeviceKey());
2169 // if (last == null ||
2170 // now - last > intervalNs) {
2171 // writeUpdatedDeviceToStorage(d);
2172 // lastWriteTimes.put(d.getDeviceKey(), now);
2174 // debugCounters.updateCounter(CNT_DEVICE_STORE_THROTTLED);
2179 // * Remove the given device from the store. If only some entities have
2180 // * been removed the updated device should be written using
2181 // * {@link #storeDevice(Device)}
2184 // public void removeDevice(Device d) {
2187 // // FIXME: could we have a problem with concurrent put to the
2188 // // hashMap? I.e., we write a stale entry to the map after the
2189 // // delete and now are left with an entry we'll never clean up
2190 // lastWriteTimes.remove(d.getDeviceKey());
2192 // // TODO: should probably do versioned delete. OTOH, even
2193 // // if we accidentally delete, we'll write it again after
2194 // // the next entity ....
2195 // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2196 // storeClient.delete(DeviceSyncRepresentation.computeKey(d));
2197 // } catch(ObsoleteVersionException e) {
2199 // } catch (SyncException e) {
2200 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2201 // logger.error("Could not remove device " + d + " from store", e);
2206 // * Remove the given Versioned device from the store. If the device
2207 // * was locally modified ignore the delete request.
2208 // * @param syncedDeviceKey
2210 // private void removeDevice(Versioned<DeviceSyncRepresentation> dev) {
2212 // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
2213 // storeClient.delete(dev.getValue().getKey(),
2214 // dev.getVersion());
2215 // } catch(ObsoleteVersionException e) {
2216 // // Key was locally modified by another thread.
2217 // // Do not delete and ignore.
2218 // } catch(SyncException e) {
2219 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2220 // logger.error("Failed to remove device entry for " +
2221 // dev.toString() + " from store.", e);
2226 // * Synchronously transition from SLAVE to MASTER. By iterating through
2227 // * the store and learning all devices from the store
2229 // private void goToMaster() {
2230 // if (logger.isDebugEnabled()) {
2231 // logger.debug("Transitioning to MASTER role");
2233 // debugCounters.updateCounter(CNT_TRANSITION_TO_MASTER);
2234 // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2237 // iter = storeClient.entries();
2238 // } catch (SyncException e) {
2239 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2240 // logger.error("Failed to read devices from sync store", e);
2244 // while(iter.hasNext()) {
2245 // Versioned<DeviceSyncRepresentation> versionedDevice =
2246 // iter.next().getValue();
2247 // DeviceSyncRepresentation storedDevice =
2248 // versionedDevice.getValue();
2249 // if (storedDevice == null)
2251 // debugCounters.updateCounter(CNT_DEVICES_FROM_STORE);
2252 // for(SyncEntity se: storedDevice.getEntities()) {
2253 // learnDeviceByEntity(se.asEntity());
2257 // if (iter != null)
2260 // storeConsolidateTask.reschedule(initialSyncStoreConsolidateMs,
2261 // TimeUnit.MILLISECONDS);
2265 // * Actually perform the write of the device to the store
2266 // * FIXME: concurrent modification behavior
2267 // * @param device The device to write
2269 // private void writeUpdatedDeviceToStorage(Device device) {
2271 // debugCounters.updateCounter(CNT_DEVICE_STORED);
2272 // // FIXME: use a versioned put
2273 // DeviceSyncRepresentation storeDevice =
2274 // new DeviceSyncRepresentation(device);
2275 // storeClient.put(storeDevice.getKey(), storeDevice);
2276 // } catch (ObsoleteVersionException e) {
2277 // // FIXME: what's the right behavior here. Can the store client
2278 // // even throw this error?
2279 // } catch (SyncException e) {
2280 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2281 // logger.error("Could not write device " + device +
2282 // " to sync store:", e);
2287 // * Iterate through all entries in the sync store. For each device
2288 // * in the store check if any stored entity matches a live device. If
2289 // * no entities match a live device we remove the entry from the store.
2291 // * Note: we do not check if all devices known to device manager are
2292 // * in the store. We rely on regular packetIns for that.
2293 // * Note: it's possible that multiple entries in the store map to the
2294 // * same device. We don't check or handle this case.
2296 // * We need to perform this check after a SLAVE->MASTER transition to
2297 // * get rid of all entries the old master might have written to the
2298 // * store after we took over. We also run it regularly in MASTER
2299 // * state to ensure we don't have stale entries in the store
2301 // private void consolidateStore() {
2304 // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_RUNS);
2305 // if (logger.isDebugEnabled()) {
2306 // logger.debug("Running consolidateStore.");
2308 // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
2311 // iter = storeClient.entries();
2312 // } catch (SyncException e) {
2313 // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
2314 // logger.error("Failed to read devices from sync store", e);
2318 // while(iter.hasNext()) {
2319 // boolean found = false;
2320 // Versioned<DeviceSyncRepresentation> versionedDevice =
2321 // iter.next().getValue();
2322 // DeviceSyncRepresentation storedDevice =
2323 // versionedDevice.getValue();
2324 // if (storedDevice == null)
2326 // for(SyncEntity se: storedDevice.getEntities()) {
2328 // // Do we have a device for this entity??
2329 // IDevice d = findDevice(se.macAddress, se.vlan,
2337 // } catch (IllegalArgumentException e) {
2338 // // not all key fields provided. Skip entity
2342 // // We currently DO NOT have a live device that
2343 // // matches the current device from the store.
2344 // // Delete device from store.
2345 // if (logger.isDebugEnabled()) {
2346 // logger.debug("Removing device {} from store. No "
2347 // + "corresponding live device",
2348 // storedDevice.getKey());
2350 // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED);
2351 // removeDevice(versionedDevice);
2355 // if (iter != null)
2363 // * For testing. Sets the syncService. Only call after init but before
2364 // * startUp. Used by MockDeviceManager
2365 // * @param syncService
2367 // protected void setSyncServiceIfNotSet(ISyncService syncService) {
2368 // if (this.syncService == null)
2369 // this.syncService = syncService;