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 private ITopologyManager topology;
125 private ISwitchManager switchManager = null;
126 private IDataPacketService dataPacketService = null;
128 public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
129 public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
130 + "-reconcileRequest";
131 public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
132 + "-reconcileNoSourceDevice";
133 public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
134 + "-reconcileNoDestDevice";
135 public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
136 + "-broadcastSource";
137 public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
138 public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
139 public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
140 + "-dhcpClientNameSnooped";
141 public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
142 + "-deviceOnInternalPortNotLearned";
143 public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
144 + "-packetNotAllowed";
145 public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
146 public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
147 + "-packetOnInternalPortForKnownDevice";
148 public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
149 public static final String CNT_DEVICE_CHANGED = MODULE_NAME
151 public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
152 public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
153 + "-cleanupEntitiesRuns";
154 public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
155 + "-entityRemovedTimeout";
156 public static final String CNT_DEVICE_DELETED = MODULE_NAME
158 public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
159 + "-deviceReclassifyDelete";
160 public static final String CNT_DEVICE_STORED = MODULE_NAME
162 public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
163 + "-deviceStoreThrottled";
164 public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
165 + "-deviceRemovedFromStore";
166 public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
168 public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
169 + "-devicesFromStore";
170 public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
171 + "-consolidateStoreRuns";
172 public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
173 + "-consolidateStoreDevicesRemoved";
175 static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
176 .getCanonicalName() + ".stateStore";
180 * Time in milliseconds before entities will expire
182 protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
185 * Time in seconds between cleaning up old entities/devices
187 protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
190 * This is the master device map that maps device IDs to {@link Device}
193 protected ConcurrentHashMap<Long, Device> deviceMap;
195 protected ConcurrentHashMap<NodeConnector, Entity> inactiveStaticDevices;
197 * Counter used to generate device keys
199 protected long deviceKeyCounter = 0;
202 * Lock for incrementing the device key counter
204 protected Object deviceKeyLock = new Object();
207 * This is the primary entity index that contains all entities
209 protected DeviceUniqueIndex primaryIndex;
212 * This stores secondary indices over the fields in the devices
214 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
217 * This map contains state for each of the {@ref IEntityClass} that exist
219 protected ConcurrentHashMap<String, ClassState> classStateMap;
222 * This is the list of indices we want on a per-class basis
224 protected Set<EnumSet<DeviceField>> perClassIndices;
227 * The entity classifier currently in use
229 protected IEntityClassifierService entityClassifier;
232 * Used to cache state about specific entity classes
234 protected class ClassState {
239 protected DeviceUniqueIndex classIndex;
242 * This stores secondary indices over the fields in the device for the
245 protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
248 * Allocate a new {@link ClassState} object for the class
251 * the class to use for the state
253 public ClassState(IEntityClass clazz) {
254 EnumSet<DeviceField> keyFields = clazz.getKeyFields();
255 EnumSet<DeviceField> primaryKeyFields = entityClassifier
257 boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
259 if (!keyFieldsMatchPrimary)
260 classIndex = new DeviceUniqueIndex(keyFields);
262 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
263 for (EnumSet<DeviceField> fields : perClassIndices) {
264 secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
270 * Device manager event listeners reclassifyDeviceListeners are notified
271 * first before reconcileDeviceListeners. This is to make sure devices are
272 * correctly reclassified before reconciliation.
274 protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
277 * Using the IfNewHostNotify to notify listeners of host changes.
279 private Set<IfNewHostNotify> newHostNotify = Collections
280 .synchronizedSet(new HashSet<IfNewHostNotify>());
283 * A device update event to be dispatched
285 protected static class DeviceUpdate {
291 * The affected device
293 protected Device device;
296 * The change that was made
298 protected Change change;
301 * If not added, then this is the list of fields changed
303 protected EnumSet<DeviceField> fieldsChanged;
305 public DeviceUpdate(Device device, Change change,
306 EnumSet<DeviceField> fieldsChanged) {
308 this.device = device;
309 this.change = change;
310 this.fieldsChanged = fieldsChanged;
314 public String toString() {
315 String devIdStr = device.getEntityClass().getName() + "::"
316 + device.getMACAddressString();
317 return "DeviceUpdate [device=" + devIdStr + ", change=" + change
318 + ", fieldsChanged=" + fieldsChanged + "]";
324 * AttachmentPointComparator
326 * Compares two attachment points and returns the latest one. It is assumed
327 * that the two attachment points are in the same L2 domain.
331 protected class AttachmentPointComparator implements
332 Comparator<AttachmentPoint> {
333 public AttachmentPointComparator() {
338 public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
339 // First compare based on L2 domain ID;
341 // XXX - missing functionality -- need topology
342 // long oldDomain = topology.getL2DomainId(oldSw);
343 // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
345 boolean oldBD = false;
347 // XXX - missing functionality -- need topology
348 // long newDomain = topology.getL2DomainId(newSw);
349 // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
351 boolean newBD = false;
353 if (oldDomain < newDomain) {
355 } else if (oldDomain > newDomain) {
358 // Give preference to OFPP_LOCAL always
359 if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
360 && newAP.getPort().getType()
361 .equals(NodeConnectorIDType.SWSTACK)) {
363 } else if (oldAP.getPort().getType()
364 .equals(NodeConnectorIDType.SWSTACK)
365 && !newAP.getPort().getType()
366 .equals(NodeConnectorIDType.SWSTACK)) {
370 // We expect that the last seen of the new AP is higher than
371 // old AP, if it is not, just reverse and send the negative
373 if (oldAP.getActiveSince() > newAP.getActiveSince())
374 return -compare(newAP, oldAP);
376 long activeOffset = 0;
378 if (!newBD && oldBD) {
381 if (newBD && oldBD) {
382 activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
383 } else if (newBD && !oldBD) {
384 activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
388 if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
389 || (newAP.getLastSeen() > oldAP.getLastSeen()
390 + AttachmentPoint.INACTIVITY_INTERVAL)) {
398 * Comparator for sorting by cluster ID
400 public AttachmentPointComparator apComparator;
403 * Switch ports where attachment points shouldn't be learned
405 private Set<SwitchPort> suppressAPs;
408 * Periodic task to clean up expired entities
410 public SingletonTask entityCleanupTask;
412 // ********************
413 // Dependency injection
414 // ********************
416 void setNewHostNotify(IfNewHostNotify obj) {
417 this.newHostNotify.add(obj);
420 void unsetNewHostNotify(IfNewHostNotify obj) {
421 this.newHostNotify.remove(obj);
424 void setDataPacketService(IDataPacketService s) {
425 this.dataPacketService = s;
428 void unsetDataPacketService(IDataPacketService s) {
429 if (this.dataPacketService == s) {
430 this.dataPacketService = null;
434 public void setTopologyManager(ITopologyManager s) {
438 public void unsetTopologyManager(ITopologyManager s) {
439 if (this.topology == s) {
440 logger.debug("Topology Manager Service removed!");
441 this.topology = null;
445 private volatile boolean stopped = true;
446 private ScheduledExecutorService ses;
454 public void start() {
455 this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
457 // XXX - TODO need to make it possible to register a non-default
459 entityClassifier = new DefaultEntityClassifier();
460 this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
461 this.suppressAPs = Collections
462 .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
463 primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
464 secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
466 deviceMap = new ConcurrentHashMap<Long, Device>();
467 inactiveStaticDevices = new ConcurrentHashMap<NodeConnector, Entity>();
468 classStateMap = new ConcurrentHashMap<String, ClassState>();
469 apComparator = new AttachmentPointComparator();
471 addIndex(true, EnumSet.of(DeviceField.IPV4));
474 // XXX - Should use a common threadpool but this doesn't currently exist
475 ses = Executors.newScheduledThreadPool(1);
476 Runnable ecr = new Runnable() {
481 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
485 entityCleanupTask = new SingletonTask(ses, ecr);
486 entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
488 registerDeviceManagerDebugCounters();
492 * Periodic task to consolidate entries in the store. I.e., delete entries
493 * in the store that are not known to DeviceManager
495 // XXX - Missing functionality
496 // private SingletonTask storeConsolidateTask;
498 // *********************
499 // IDeviceManagerService
500 // *********************
502 void setSwitchManager(ISwitchManager s) {
503 logger.debug("SwitchManager set");
504 this.switchManager = s;
507 void unsetSwitchManager(ISwitchManager s) {
508 if (this.switchManager == s) {
509 logger.debug("SwitchManager removed!");
510 this.switchManager = null;
515 public IDevice getDevice(Long deviceKey) {
516 return deviceMap.get(deviceKey);
520 public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
521 NodeConnector port) throws IllegalArgumentException {
522 if (vlan != null && vlan.shortValue() <= 0)
524 if (ipv4Address != null && ipv4Address == 0)
526 Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
527 if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
528 throw new IllegalArgumentException("Not all key fields specified."
529 + " Required fields: " + entityClassifier.getKeyFields());
531 return findDeviceByEntity(e);
535 public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
536 Short vlan, Integer ipv4Address) throws IllegalArgumentException {
537 if (vlan != null && vlan.shortValue() <= 0)
539 if (ipv4Address != null && ipv4Address == 0)
541 Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
542 if (entityClass == null
543 || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
544 throw new IllegalArgumentException("Not all key fields and/or "
545 + " no source device specified. Required fields: "
546 + entityClassifier.getKeyFields());
548 return findDestByEntity(entityClass, e);
552 public Collection<? extends IDevice> getAllDevices() {
553 return Collections.unmodifiableCollection(deviceMap.values());
557 public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
559 perClassIndices.add(keyFields);
561 secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
566 public Iterator<? extends IDevice> queryDevices(Long macAddress,
567 Short vlan, Integer ipv4Address, NodeConnector port) {
568 DeviceIndex index = null;
569 if (secondaryIndexMap.size() > 0) {
570 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
572 index = secondaryIndexMap.get(keys);
575 Iterator<Device> deviceIterator = null;
577 // Do a full table scan
578 deviceIterator = deviceMap.values().iterator();
581 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
582 vlan, ipv4Address, port, null);
583 deviceIterator = new DeviceIndexInterator(this,
584 index.queryByEntity(entity));
587 DeviceIterator di = new DeviceIterator(deviceIterator, null,
588 macAddress, vlan, ipv4Address, port);
593 public Iterator<? extends IDevice> queryClassDevices(
594 IEntityClass entityClass, Long macAddress, Short vlan,
595 Integer ipv4Address, NodeConnector port) {
596 ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
597 ClassState classState = getClassState(entityClass);
599 DeviceIndex index = null;
600 if (classState.secondaryIndexMap.size() > 0) {
601 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
603 index = classState.secondaryIndexMap.get(keys);
606 Iterator<Device> iter;
608 index = classState.classIndex;
611 return new DeviceIterator(deviceMap.values().iterator(),
612 new IEntityClass[] { entityClass }, macAddress, vlan,
615 // scan the entire class
616 iter = new DeviceIndexInterator(this, index.getAll());
620 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
621 vlan, ipv4Address, port, null);
622 iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
626 return new MultiIterator<Device>(iterators.iterator());
629 protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
630 Short vlan, Integer ipv4Address, NodeConnector port) {
631 DeviceIndex index = null;
632 if (secondaryIndexMap.size() > 0) {
633 EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
635 index = secondaryIndexMap.get(keys);
638 Iterator<Device> deviceIterator = null;
640 // Do a full table scan
641 deviceIterator = deviceMap.values().iterator();
644 Entity entity = new Entity((macAddress == null ? 0 : macAddress),
645 vlan, ipv4Address, port, null);
646 deviceIterator = new DeviceIndexInterator(this,
647 index.queryByEntity(entity));
650 DeviceIterator di = new DeviceIterator(deviceIterator, null,
651 macAddress, vlan, ipv4Address, port);
656 public void addListener(IDeviceListener listener) {
657 deviceListeners.addListener("device", listener);
662 public void addSuppressAPs(NodeConnector port) {
663 suppressAPs.add(new SwitchPort(port));
667 public void removeSuppressAPs(NodeConnector port) {
668 suppressAPs.remove(new SwitchPort(port));
672 public Set<SwitchPort> getSuppressAPs() {
673 return Collections.unmodifiableSet(suppressAPs);
676 private void logListeners() {
677 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
678 if (listeners != null) {
679 StringBuffer sb = new StringBuffer();
680 sb.append("DeviceListeners: ");
681 for (IDeviceListener l : listeners) {
682 sb.append(l.getName());
685 logger.debug(sb.toString());
695 public PacketResult receiveDataPacket(RawPacket inPkt) {
696 // XXX - Can this really pass in null? Why would you ever want that?
698 return PacketResult.IGNORED;
701 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
703 if (formattedPak instanceof Ethernet) {
704 eth = (Ethernet) formattedPak;
706 return PacketResult.IGNORED;
709 // Extract source entity information
710 NodeConnector inPort = inPkt.getIncomingNodeConnector();
711 Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
712 if (srcEntity == null) {
713 return PacketResult.CONSUME;
716 // Learn from ARP packet for special VRRP settings.
717 // In VRRP settings, the source MAC address and sender MAC
718 // addresses can be different. In such cases, we need to learn
719 // the IP to MAC mapping of the VRRP IP address. The source
720 // entity will not have that information. Hence, a separate call
721 // to learn devices in such cases.
722 learnDeviceFromArpResponseData(eth, inPort);
724 // Learn/lookup device information
725 Device srcDevice = learnDeviceByEntity(srcEntity);
726 if (srcDevice == null) {
727 return PacketResult.CONSUME;
729 logger.trace("Saw packet from device {}", srcDevice);
731 return PacketResult.KEEP_PROCESSING;
740 * Check whether the given attachment point is valid given the current
747 * @return true if it's a valid attachment point
749 public boolean isValidAttachmentPoint(NodeConnector port) {
750 // XXX - missing functionality -- need topology module
751 // if (topology.isAttachmentPointPort(port) == false)
753 if (topology.isInternal(port))
755 if (!switchManager.isNodeConnectorEnabled(port))
757 if (suppressAPs.contains(new SwitchPort(port)))
764 * Get sender IP address from packet if the packet is either an ARP packet.
770 private int getSrcNwAddr(Ethernet eth, long dlAddr) {
771 if (eth.getPayload() instanceof ARP) {
772 ARP arp = (ARP) eth.getPayload();
773 if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
774 && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
775 return toIPv4Address(arp.getSenderProtocolAddress());
782 * Parse an entity from an {@link Ethernet} packet.
785 * the packet to parse
787 * the switch on which the packet arrived
789 * the original packetin
790 * @return the entity from the packet
792 protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
793 byte[] dlAddrArr = eth.getSourceMACAddress();
794 long dlAddr = toLong(dlAddrArr);
796 // Ignore broadcast/multicast source
797 if ((dlAddrArr[0] & 0x1) != 0)
800 // XXX missing functionality
802 int nwSrc = getSrcNwAddr(eth, dlAddr);
803 return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
808 * Learn device from ARP data in scenarios where the Ethernet source MAC is
809 * different from the sender hardware address in ARP data.
811 protected void learnDeviceFromArpResponseData(Ethernet eth,
812 NodeConnector port) {
814 if (!(eth.getPayload() instanceof ARP))
816 ARP arp = (ARP) eth.getPayload();
818 byte[] dlAddrArr = eth.getSourceMACAddress();
819 long dlAddr = toLong(dlAddrArr);
821 byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
822 long senderAddr = toLong(senderHardwareAddr);
824 if (dlAddr == senderAddr)
827 // Ignore broadcast/multicast source
828 if ((senderHardwareAddr[0] & 0x1) != 0)
831 // short vlan = eth.getVlanID();
832 int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
834 Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
837 learnDeviceByEntity(e);
841 * Look up a {@link Device} based on the provided {@link Entity}. We first
842 * check the primary index. If we do not find an entry there we classify the
843 * device into its IEntityClass and query the classIndex. This implies that
844 * all key field of the current IEntityClassifier must be present in the
845 * entity for the lookup to succeed!
848 * the entity to search for
849 * @return The {@link Device} object if found
851 protected Device findDeviceByEntity(Entity entity) {
852 // Look up the fully-qualified entity to see if it already
853 // exists in the primary entity index.
854 Long deviceKey = primaryIndex.findByEntity(entity);
855 IEntityClass entityClass = null;
857 if (deviceKey == null) {
858 // If the entity does not exist in the primary entity index,
859 // use the entity classifier for find the classes for the
860 // entity. Look up the entity in the returned class'
861 // class entity index.
862 entityClass = entityClassifier.classifyEntity(entity);
863 if (entityClass == null) {
866 ClassState classState = getClassState(entityClass);
868 if (classState.classIndex != null) {
869 deviceKey = classState.classIndex.findByEntity(entity);
872 if (deviceKey == null)
874 return deviceMap.get(deviceKey);
878 * Get a destination device using entity fields that corresponds with the
879 * given source device. The source device is important since there could be
880 * ambiguity in the destination device without the attachment point
884 * the source device's entity class. The returned destination
885 * will be in the same entity class as the source.
887 * the entity to look up
888 * @return an {@link Device} or null if no device is found.
890 protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
892 // Look up the fully-qualified entity to see if it
893 // exists in the primary entity index
894 Long deviceKey = primaryIndex.findByEntity(dstEntity);
896 if (deviceKey == null) {
897 // This could happen because:
898 // 1) no destination known, or a broadcast destination
899 // 2) if we have attachment point key fields since
900 // attachment point information isn't available for
901 // destination devices.
902 // For the second case, we'll need to match up the
903 // destination device with the class of the source
905 ClassState classState = getClassState(reference);
906 if (classState.classIndex == null) {
909 deviceKey = classState.classIndex.findByEntity(dstEntity);
911 if (deviceKey == null)
913 return deviceMap.get(deviceKey);
917 * Look up a {@link Device} within a particular entity class based on the
918 * provided {@link Entity}.
921 * the entity class to search for the entity
923 * the entity to search for
924 * @return The {@link Device} object if found private Device
925 * findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
926 * XXX - TODO throw new UnsupportedOperationException(); }
930 * Look up a {@link Device} based on the provided {@link Entity}. Also
931 * learns based on the new entity, and will update existing devices as
936 * @return The {@link Device} object if found
938 protected Device learnDeviceByEntity(Entity entity) {
939 logger.info("Primary index {}", primaryIndex);
940 ArrayList<Long> deleteQueue = null;
941 LinkedList<DeviceUpdate> deviceUpdates = null;
942 Device oldDevice = null;
943 Device device = null;
945 // we may need to restart the learning process if we detect
946 // concurrent modification. Note that we ensure that at least
947 // one thread should always succeed so we don't get into infinite
950 deviceUpdates = null;
952 // Look up the fully-qualified entity to see if it already
953 // exists in the primary entity index.
954 Long deviceKey = primaryIndex.findByEntity(entity);
955 IEntityClass entityClass = null;
957 if (deviceKey == null) {
958 // If the entity does not exist in the primary entity index,
959 // use the entity classifier for find the classes for the
960 // entity. Look up the entity in the returned class'
961 // class entity index.
962 entityClass = entityClassifier.classifyEntity(entity);
963 if (entityClass == null) {
964 // could not classify entity. No device
968 ClassState classState = getClassState(entityClass);
970 if (classState.classIndex != null) {
971 deviceKey = classState.classIndex.findByEntity(entity);
974 if (deviceKey != null) {
975 // If the primary or secondary index contains the entity
976 // use resulting device key to look up the device in the
977 // device map, and use the referenced Device below.
978 device = deviceMap.get(deviceKey);
979 if (device == null) {
980 // This can happen due to concurrent modification
981 if (logger.isDebugEnabled()) {
982 logger.debug("No device for deviceKey {} while "
983 + "while processing entity {}", deviceKey,
986 // if so, then try again till we don't even get the device
988 // and so we recreate the device
992 // If the secondary index does not contain the entity,
993 // create a new Device object containing the entity, and
994 // generate a new device ID if the the entity is on an
995 // attachment point port. Otherwise ignore.
996 if (entity.hasSwitchPort()
997 && !isValidAttachmentPoint(entity.getPort())) {
998 // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
999 if (logger.isDebugEnabled()) {
1000 logger.debug("Not learning new device on internal"
1001 + " link: {}", entity);
1006 // Before we create the new device also check if
1007 // the entity is allowed (e.g., for spoofing protection)
1008 if (!isEntityAllowed(entity, entityClass)) {
1009 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1010 if (logger.isDebugEnabled()) {
1011 logger.debug("PacketIn is not allowed {} {}",
1012 entityClass.getName(), entity);
1017 synchronized (deviceKeyLock) {
1018 deviceKey = Long.valueOf(deviceKeyCounter++);
1020 device = allocateDevice(deviceKey, entity, entityClass);
1022 // Add the new device to the primary map with a simple put
1023 deviceMap.put(deviceKey, device);
1026 if (!updateIndices(device, deviceKey)) {
1027 if (deleteQueue == null)
1028 deleteQueue = new ArrayList<Long>();
1029 deleteQueue.add(deviceKey);
1033 updateSecondaryIndices(entity, entityClass, deviceKey);
1035 // We need to count and log here. If we log earlier we could
1036 // hit a concurrent modification and restart the dev creation
1037 // and potentially count the device twice.
1038 // debugCounters.updateCounter(CNT_NEW_DEVICE);
1039 if (logger.isDebugEnabled()) {
1041 "New device created: {} deviceKey={}, entity={}",
1042 new Object[] { device, deviceKey, entity });
1044 // generate new device update
1045 deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
1046 device, ADD, null));
1050 // if it gets here, we have a pre-existing Device for this Entity
1051 if (!isEntityAllowed(entity, device.getEntityClass())) {
1052 // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
1053 if (logger.isDebugEnabled()) {
1054 logger.info("PacketIn is not allowed {} {}", device
1055 .getEntityClass().getName(), entity);
1059 // If this is not an attachment point port we don't learn the new
1061 // and don't update indexes. But we do allow the device to continue
1064 if (entity.hasSwitchPort()
1065 && !isValidAttachmentPoint(entity.getPort())) {
1066 // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
1069 int entityindex = -1;
1070 if ((entityindex = device.entityIndex(entity)) >= 0) {
1071 // Entity already exists
1072 // update timestamp on the found entity
1073 Date lastSeen = entity.getLastSeenTimestamp();
1074 if (lastSeen == null) {
1075 lastSeen = new Date();
1076 entity.setLastSeenTimestamp(lastSeen);
1078 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
1079 // we break the loop after checking for changes to the AP
1081 // New entity for this device
1082 // compute the insertion point for the entity.
1083 // see Arrays.binarySearch()
1084 entityindex = -(entityindex + 1);
1085 Device newDevice = allocateDevice(device, entity, entityindex);
1088 EnumSet<DeviceField> changedFields = findChangedFields(device,
1091 // update the device map with a replace call
1092 boolean res = deviceMap.replace(deviceKey, device, newDevice);
1093 // If replace returns false, restart the process from the
1094 // beginning (this implies another thread concurrently
1095 // modified this Device).
1101 if (!updateIndices(device, deviceKey)) {
1104 updateSecondaryIndices(entity, device.getEntityClass(),
1107 // We need to count here after all the possible "continue"
1108 // statements in this branch
1109 // debugCounters.updateCounter(CNT_NEW_ENTITY);
1110 if (changedFields.size() > 0) {
1111 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1112 deviceUpdates = updateUpdates(deviceUpdates,
1113 new DeviceUpdate(newDevice, CHANGE, changedFields));
1115 // we break the loop after checking for changed AP
1117 // Update attachment point (will only be hit if the device
1118 // already existed and no concurrent modification)
1119 if (entity.hasSwitchPort()) {
1120 boolean moved = device.updateAttachmentPoint(entity.getPort(),
1121 entity.getLastSeenTimestamp().getTime());
1122 // TODO: use update mechanism instead of sending the
1123 // notification directly
1125 // we count device moved events in
1126 // sendDeviceMovedNotification()
1127 sendDeviceMovedNotification(device, oldDevice);
1128 if (logger.isTraceEnabled()) {
1129 logger.trace("Device moved: attachment points {},"
1130 + "entities {}", device.attachmentPoints,
1134 if (logger.isTraceEnabled()) {
1135 logger.trace("Device attachment point updated: "
1136 + "attachment points {}," + "entities {}",
1137 device.attachmentPoints, device.entities);
1144 if (deleteQueue != null) {
1145 for (Long l : deleteQueue) {
1146 Device dev = deviceMap.get(l);
1147 this.deleteDevice(dev);
1151 processUpdates(deviceUpdates);
1152 // deviceSyncManager.storeDeviceThrottled(device);
1157 protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
1161 protected EnumSet<DeviceField> findChangedFields(Device device,
1163 EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
1164 DeviceField.VLAN, DeviceField.SWITCHPORT);
1166 if (newEntity.getIpv4Address() == null)
1167 changedFields.remove(DeviceField.IPV4);
1168 if (newEntity.getVlan() == null)
1169 changedFields.remove(DeviceField.VLAN);
1170 if (newEntity.getPort() == null)
1171 changedFields.remove(DeviceField.SWITCHPORT);
1173 if (changedFields.size() == 0)
1174 return changedFields;
1176 for (Entity entity : device.getEntities()) {
1177 if (newEntity.getIpv4Address() == null
1178 || (entity.getIpv4Address() != null && entity
1180 .equals(newEntity.getIpv4Address())))
1181 changedFields.remove(DeviceField.IPV4);
1182 if (newEntity.getVlan() == null
1183 || (entity.getVlan() != null && entity.getVlan().equals(
1184 newEntity.getVlan())))
1185 changedFields.remove(DeviceField.VLAN);
1186 if (newEntity.getPort() == null
1187 || (entity.getPort() != null && entity.getPort().equals(
1188 newEntity.getPort())))
1189 changedFields.remove(DeviceField.SWITCHPORT);
1192 return changedFields;
1196 * Send update notifications to listeners
1199 * the updates to process.
1201 protected void processUpdates(Queue<DeviceUpdate> updates) {
1202 if (updates == null)
1204 DeviceUpdate update = null;
1205 while (null != (update = updates.poll())) {
1206 if (logger.isTraceEnabled()) {
1207 logger.trace("Dispatching device update: {}", update);
1209 // if (update.change == DeviceUpdate.Change.DELETE)
1210 // deviceSyncManager.removeDevice(update.device);
1212 // deviceSyncManager.storeDevice(update.device);
1213 List<IDeviceListener> listeners = deviceListeners
1214 .getOrderedListeners();
1215 notifyListeners(listeners, update);
1219 protected void notifyListeners(List<IDeviceListener> listeners,
1220 DeviceUpdate update) {
1221 // Topology update is for some reason outside of listeners registry
1223 Entity[] ents = update.device.getEntities();
1224 Entity e = ents[ents.length - 1];
1226 NodeConnector p = e.getPort();
1227 Node node = p.getNode();
1231 byte[] mac = NetUtils.longToByteArray6(e.getMacAddress());
1232 DataLinkAddress dla = new EthernetAddress(
1235 InetAddress.getAllByName(e.getIpv4Address().toString());
1236 h = new org.opendaylight.controller.sal.core.Host(dla,
1237 InetAddress.getByName(e.getIpv4Address().toString()));
1238 } catch (ConstructionException ce) {
1241 } catch (UnknownHostException ue) {
1246 if (topology != null && p != null && h != null) {
1247 if (update.change.equals(DeviceUpdate.Change.ADD)) {
1248 Tier tier = new Tier(1);
1249 switchManager.setNodeProp(node, tier);
1250 topology.updateHostLink(p, h, UpdateType.ADDED, null);
1252 // No need to reset the tiering if no other hosts are currently
1254 // If this switch was discovered to be an access switch, it
1255 // still is even if the host is down
1256 Tier tier = new Tier(0);
1257 switchManager.setNodeProp(node, tier);
1258 topology.updateHostLink(p, h, UpdateType.REMOVED, null);
1262 if (listeners == null && newHostNotify.isEmpty()) {
1266 * TODO: IfNewHostNotify is needed for current controller API. Adding
1267 * logic so that existing apps (like SimpleForwardingManager) work.
1268 * IDeviceListener adds additional methods and uses IListener's callback
1269 * ordering. The two interfaces need to be merged.
1272 for (IfNewHostNotify notify : newHostNotify) {
1273 switch (update.change) {
1275 notify.notifyHTClient(update.device.toHostNodeConnector());
1278 notify.notifyHTClientHostRemoved(update.device
1279 .toHostNodeConnector());
1286 * TODO: Remove this section as IDeviceListener functionality gets
1287 * merged with IfNewHostNotify
1289 for (IDeviceListener listener : listeners) {
1290 switch (update.change) {
1292 listener.deviceAdded(update.device);
1295 listener.deviceRemoved(update.device);
1298 for (DeviceField field : update.fieldsChanged) {
1301 listener.deviceIPV4AddrChanged(update.device);
1304 // listener.deviceMoved(update.device);
1307 listener.deviceVlanChanged(update.device);
1310 logger.debug("Unknown device field changed {}",
1311 update.fieldsChanged.toString());
1321 * Check if the entity e has all the keyFields set. Returns false if not
1326 * the key fields to check e against
1329 protected boolean allKeyFieldsPresent(Entity e,
1330 EnumSet<DeviceField> keyFields) {
1331 for (DeviceField f : keyFields) {
1334 // MAC address is always present
1337 if (e.getIpv4Address() == null)
1341 if (e.getPort() == null)
1345 // FIXME: vlan==null is ambiguous: it can mean: not present
1347 // if (e.vlan == null) return false;
1350 // we should never get here. unless somebody extended
1352 throw new IllegalStateException();
1358 private LinkedList<DeviceUpdate> updateUpdates(
1359 LinkedList<DeviceUpdate> list, DeviceUpdate update) {
1363 list = new LinkedList<DeviceUpdate>();
1370 * Get the secondary index for a class. Will return null if the secondary
1371 * index was created concurrently in another thread.
1374 * the class for the index
1377 private ClassState getClassState(IEntityClass clazz) {
1378 ClassState classState = classStateMap.get(clazz.getName());
1379 if (classState != null)
1382 classState = new ClassState(clazz);
1383 ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
1392 * Update both the primary and class indices for the provided device. If the
1393 * update fails because of an concurrent update, will return false.
1396 * the device to update
1398 * the device key for the device
1399 * @return true if the update succeeded, false otherwise.
1401 private boolean updateIndices(Device device, Long deviceKey) {
1402 if (!primaryIndex.updateIndex(device, deviceKey)) {
1405 IEntityClass entityClass = device.getEntityClass();
1406 ClassState classState = getClassState(entityClass);
1408 if (classState.classIndex != null) {
1409 if (!classState.classIndex.updateIndex(device, deviceKey))
1416 * Update the secondary indices for the given entity and associated entity
1420 * the entity to update
1421 * @param entityClass
1422 * the entity class for the entity
1424 * the device key to set up
1426 private void updateSecondaryIndices(Entity entity,
1427 IEntityClass entityClass, Long deviceKey) {
1428 for (DeviceIndex index : secondaryIndexMap.values()) {
1429 index.updateIndex(entity, deviceKey);
1431 ClassState state = getClassState(entityClass);
1432 for (DeviceIndex index : state.secondaryIndexMap.values()) {
1433 index.updateIndex(entity, deviceKey);
1438 * Clean up expired entities/devices
1440 protected void cleanupEntities() {
1441 // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
1443 Calendar c = Calendar.getInstance();
1444 c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
1445 Date cutoff = c.getTime();
1447 ArrayList<Entity> toRemove = new ArrayList<Entity>();
1448 ArrayList<Entity> toKeep = new ArrayList<Entity>();
1450 Iterator<Device> diter = deviceMap.values().iterator();
1451 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1453 while (diter.hasNext()) {
1454 Device d = diter.next();
1457 deviceUpdates.clear();
1460 for (Entity e : d.getEntities()) {
1461 if (e.getLastSeenTimestamp() != null
1462 && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
1463 // individual entity needs to be removed
1469 if (toRemove.size() == 0) {
1473 // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
1474 for (Entity e : toRemove) {
1475 removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
1479 if (toKeep.size() > 0) {
1480 Device newDevice = allocateDevice(d.getDeviceKey(),
1481 d.getDHCPClientName(), d.oldAPs,
1482 d.attachmentPoints, toKeep, d.getEntityClass());
1484 EnumSet<DeviceField> changedFields = EnumSet
1485 .noneOf(DeviceField.class);
1486 for (Entity e : toRemove) {
1487 changedFields.addAll(findChangedFields(newDevice, e));
1489 DeviceUpdate update = null;
1490 if (changedFields.size() > 0) {
1491 update = new DeviceUpdate(d, CHANGE, changedFields);
1494 if (!deviceMap.replace(newDevice.getDeviceKey(), d,
1496 // concurrent modification; try again
1497 // need to use device that is the map now for the next
1499 d = deviceMap.get(d.getDeviceKey());
1503 if (update != null) {
1504 // need to count after all possibly continue stmts in
1506 // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
1507 deviceUpdates.add(update);
1510 DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
1511 if (!deviceMap.remove(d.getDeviceKey(), d)) {
1512 // concurrent modification; try again
1513 // need to use device that is the map now for the next
1515 d = deviceMap.get(d.getDeviceKey());
1518 // debugCounters.updateCounter(CNT_DEVICE_DELETED);
1520 deviceUpdates.add(update);
1522 processUpdates(deviceUpdates);
1528 protected void removeEntity(Entity removed, IEntityClass entityClass,
1529 Long deviceKey, Collection<Entity> others) {
1530 // Don't count in this method. This method CAN BE called to clean-up
1531 // after concurrent device adds/updates and thus counting here
1533 for (DeviceIndex index : secondaryIndexMap.values()) {
1534 index.removeEntityIfNeeded(removed, deviceKey, others);
1536 ClassState classState = getClassState(entityClass);
1537 for (DeviceIndex index : classState.secondaryIndexMap.values()) {
1538 index.removeEntityIfNeeded(removed, deviceKey, others);
1541 primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
1543 if (classState.classIndex != null) {
1544 classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
1550 * method to delete a given device, remove all entities first and then
1551 * finally delete the device itself.
1555 protected void deleteDevice(Device device) {
1556 // Don't count in this method. This method CAN BE called to clean-up
1557 // after concurrent device adds/updates and thus counting here
1559 ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
1560 for (Entity entity : device.getEntities()) {
1561 this.removeEntity(entity, device.getEntityClass(),
1562 device.getDeviceKey(), emptyToKeep);
1564 if (!deviceMap.remove(device.getDeviceKey(), device)) {
1565 if (logger.isDebugEnabled())
1566 logger.debug("device map does not have this device -"
1567 + device.toString());
1571 private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
1572 Integer ipv4Address, NodeConnector port) {
1573 // FIXME: vlan==null is a valid search. Need to handle this
1574 // case correctly. Note that the code will still work correctly.
1575 // But we might do a full device search instead of using an index.
1576 EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
1577 if (macAddress != null)
1578 keys.add(DeviceField.MAC);
1580 keys.add(DeviceField.VLAN);
1581 if (ipv4Address != null)
1582 keys.add(DeviceField.IPV4);
1584 keys.add(DeviceField.SWITCHPORT);
1588 protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
1589 EnumSet<DeviceField> keyFields, Entity entity) {
1590 ClassState classState = getClassState(clazz);
1591 DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
1593 return Collections.<Device> emptySet().iterator();
1594 return new DeviceIndexInterator(this, index.queryByEntity(entity));
1597 protected Device allocateDevice(Long deviceKey, Entity entity,
1598 IEntityClass entityClass) {
1599 return new Device(this, deviceKey, entity, entityClass);
1603 protected Device allocateDevice(Long deviceKey, String dhcpClientName,
1604 List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
1605 Collection<Entity> entities, IEntityClass entityClass) {
1606 return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
1607 entities, entityClass);
1610 protected Device allocateDevice(Device device, Entity entity,
1611 int insertionpoint) {
1612 return new Device(device, entity, insertionpoint);
1616 protected Device allocateDevice(Device device, Set<Entity> entities) {
1617 List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
1618 List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
1619 for (Entity entity : entities) {
1620 if (entity.getPort() != null) {
1621 AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
1622 newPossibleAPs.add(aP);
1625 if (device.attachmentPoints != null) {
1626 for (AttachmentPoint oldAP : device.attachmentPoints) {
1627 if (newPossibleAPs.contains(oldAP)) {
1632 if (newAPs.isEmpty())
1634 Device d = new Device(this, device.getDeviceKey(),
1635 device.getDHCPClientName(), newAPs, null, entities,
1636 device.getEntityClass());
1637 d.updateAttachmentPoint();
1641 // *********************
1642 // ITopologyManagerAware
1643 // *********************
1646 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
1647 Iterator<Device> diter = deviceMap.values().iterator();
1649 while (diter.hasNext()) {
1650 Device d = diter.next();
1651 if (d.updateAttachmentPoint()) {
1652 if (logger.isDebugEnabled()) {
1653 logger.debug("Attachment point changed for device: {}", d);
1655 sendDeviceMovedNotification(d);
1661 public void edgeOverUtilized(Edge edge) {
1666 public void edgeUtilBackToNormal(Edge edge) {
1670 // *********************
1671 // IEntityClassListener
1672 // *********************
1675 public void entityClassChanged(Set<String> entityClassNames) {
1677 * iterate through the devices, reclassify the devices that belong to
1678 * these entity class names
1680 Iterator<Device> diter = deviceMap.values().iterator();
1681 while (diter.hasNext()) {
1682 Device d = diter.next();
1683 if (d.getEntityClass() == null
1684 || entityClassNames.contains(d.getEntityClass().getName()))
1685 reclassifyDevice(d);
1693 * Send update notifications to listeners
1696 * the updates to process.
1698 protected void sendDeviceMovedNotification(Device d) {
1699 // debugCounters.updateCounter(CNT_DEVICE_MOVED);
1700 // deviceSyncManager.storeDevice(d);
1701 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1702 if (listeners != null) {
1703 for (IDeviceListener listener : listeners) {
1704 listener.deviceMoved(d);
1710 * Send update notifications to listeners. IfNewHostNotify listeners need to
1711 * remove old device and add new device.
1716 protected void sendDeviceMovedNotification(Device device, Device oldDevice) {
1717 for (IfNewHostNotify notify : newHostNotify) {
1718 notify.notifyHTClientHostRemoved(oldDevice.toHostNodeConnector());
1719 notify.notifyHTClient(device.toHostNodeConnector());
1721 sendDeviceMovedNotification(device);
1725 * this method will reclassify and reconcile a device - possibilities are -
1726 * create new device(s), remove entities from this device. If the device
1727 * entity class did not change then it returns false else true.
1731 protected boolean reclassifyDevice(Device device) {
1732 // first classify all entities of this device
1733 if (device == null) {
1734 logger.debug("In reclassify for null device");
1737 boolean needToReclassify = false;
1738 for (Entity entity : device.entities) {
1739 IEntityClass entityClass = this.entityClassifier
1740 .classifyEntity(entity);
1741 if (entityClass == null || device.getEntityClass() == null) {
1742 needToReclassify = true;
1745 if (!entityClass.getName()
1746 .equals(device.getEntityClass().getName())) {
1747 needToReclassify = true;
1751 if (needToReclassify == false) {
1755 // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
1756 LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
1757 // delete this device and then re-learn all the entities
1758 this.deleteDevice(device);
1759 deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
1761 if (!deviceUpdates.isEmpty())
1762 processUpdates(deviceUpdates);
1763 for (Entity entity : device.entities) {
1764 this.learnDeviceByEntity(entity);
1770 private long toLong(byte[] address) {
1772 for (int i = 0; i < 6; i++) {
1773 long t = (address[i] & 0xffL) << ((5 - i) * 8);
1780 * Accepts an IPv4 address in a byte array and returns the corresponding
1781 * 32-bit integer value.
1786 private static int toIPv4Address(byte[] ipAddress) {
1788 for (int i = 0; i < 4; i++) {
1789 int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
1795 private void registerDeviceManagerDebugCounters() {
1797 * XXX Missing functionality if (debugCounters == null) {
1798 * logger.error("Debug Counter Service not found."); debugCounters = new
1799 * NullDebugCounter(); return; }
1800 * debugCounters.registerCounter(CNT_INCOMING,
1801 * "All incoming packets seen by this module",
1802 * CounterType.ALWAYS_COUNT);
1803 * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
1804 * "Number of flows that have been received for reconciliation by " +
1805 * "this module", CounterType.ALWAYS_COUNT);
1806 * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
1807 * "Number of flow reconcile events that failed because no source " +
1808 * "device could be identified", CounterType.WARN); // is this really a
1809 * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
1810 * "Number of flow reconcile events that failed because no " +
1811 * "destination device could be identified", CounterType.WARN); // is
1812 * this really a warning
1813 * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
1814 * "Number of packetIns that were discarded because the source " +
1815 * "MAC was broadcast or multicast", CounterType.WARN);
1816 * debugCounters.registerCounter(CNT_NO_SOURCE,
1817 * "Number of packetIns that were discarded because the " +
1818 * "could not identify a source device. This can happen if a " +
1819 * "packet is not allowed, appears on an illegal port, does not " +
1820 * "have a valid address space, etc.", CounterType.WARN);
1821 * debugCounters.registerCounter(CNT_NO_DEST,
1822 * "Number of packetIns that did not have an associated " +
1823 * "destination device. E.g., because the destination MAC is " +
1824 * "broadcast/multicast or is not yet known to the controller.",
1825 * CounterType.ALWAYS_COUNT);
1826 * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
1827 * "Number of times a DHCP client name was snooped from a " +
1828 * "packetIn.", CounterType.ALWAYS_COUNT);
1829 * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
1830 * "Number of times packetIn was received on an internal port and" +
1831 * "no source device is known for the source MAC. The packetIn is " +
1832 * "discarded.", CounterType.WARN);
1833 * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
1834 * "Number of times a packetIn was not allowed due to spoofing " +
1835 * "protection configuration.", CounterType.WARN); // is this really a
1836 * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
1837 * "Number of times a new device was learned",
1838 * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
1839 * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
1840 * "Number of times a packetIn was received on an internal port " +
1841 * "for a known device.", CounterType.ALWAYS_COUNT);
1842 * debugCounters.registerCounter(CNT_NEW_ENTITY,
1843 * "Number of times a new entity was learned for an existing device",
1844 * CounterType.ALWAYS_COUNT);
1845 * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
1846 * "Number of times device properties have changed",
1847 * CounterType.ALWAYS_COUNT);
1848 * debugCounters.registerCounter(CNT_DEVICE_MOVED,
1849 * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
1850 * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
1851 * "Number of times the entity cleanup task has been run",
1852 * CounterType.ALWAYS_COUNT);
1853 * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
1854 * "Number of times entities have been removed due to timeout " +
1855 * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
1856 * CounterType.ALWAYS_COUNT);
1857 * debugCounters.registerCounter(CNT_DEVICE_DELETED,
1858 * "Number of devices that have been removed due to inactivity",
1859 * CounterType.ALWAYS_COUNT);
1860 * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
1861 * "Number of devices that required reclassification and have been " +
1862 * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
1863 * debugCounters.registerCounter(CNT_DEVICE_STORED,
1864 * "Number of device entries written or updated to the sync store",
1865 * CounterType.ALWAYS_COUNT);
1866 * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
1867 * "Number of times a device update to the sync store was " +
1868 * "requested but not performed because the same device entities " +
1869 * "have recently been updated already", CounterType.ALWAYS_COUNT);
1870 * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
1871 * "Number of devices that were removed from the sync store " +
1872 * "because the local controller removed the device due to " +
1873 * "inactivity", CounterType.ALWAYS_COUNT);
1874 * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
1875 * "Number of times an operation on the sync store resulted in " +
1876 * "sync exception", CounterType.WARN); // it this an error?
1877 * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
1878 * "Number of devices that were read from the sync store after " +
1879 * "the local controller transitioned from SLAVE to MASTER",
1880 * CounterType.ALWAYS_COUNT);
1881 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
1882 * "Number of times the task to consolidate entries in the " +
1883 * "store witch live known devices has been run",
1884 * CounterType.ALWAYS_COUNT);
1885 * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
1886 * "Number of times a device has been removed from the sync " +
1887 * "store because no corresponding live device is known. " +
1888 * "This indicates a remote controller still writing device " +
1889 * "entries despite the local controller being MASTER or an " +
1890 * "incosistent store update from the local controller.",
1891 * CounterType.WARN);
1892 * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
1893 * "Number of times this controller has transitioned from SLAVE " +
1894 * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
1899 public HostNodeConnector hostFind(InetAddress networkAddress) {
1900 // TODO Auto-generated method stub
1905 public HostNodeConnector hostQuery(InetAddress networkAddress) {
1906 // TODO Auto-generated method stub
1911 public Future<HostNodeConnector> discoverHost(InetAddress networkAddress) {
1912 // TODO Auto-generated method stub
1917 public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress) {
1918 // TODO Auto-generated method stub
1923 public Set<HostNodeConnector> getAllHosts() {
1924 Collection<Device> devices = Collections
1925 .unmodifiableCollection(deviceMap.values());
1926 Iterator<Device> i = devices.iterator();
1927 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
1928 while (i.hasNext()) {
1929 Device device = i.next();
1930 nc.add(device.toHostNodeConnector());
1936 public Set<HostNodeConnector> getActiveStaticHosts() {
1937 Collection<Device> devices = Collections
1938 .unmodifiableCollection(deviceMap.values());
1939 Iterator<Device> i = devices.iterator();
1940 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
1941 while (i.hasNext()) {
1942 Device device = i.next();
1943 if (device.isStaticHost())
1944 nc.add(device.toHostNodeConnector());
1950 public Set<HostNodeConnector> getInactiveStaticHosts() {
1951 Collection<Entity> devices = Collections
1952 .unmodifiableCollection(inactiveStaticDevices.values());
1953 Iterator<Entity> i = devices.iterator();
1954 Set<HostNodeConnector> nc = new HashSet<HostNodeConnector>();
1955 while (i.hasNext()) {
1956 Entity ent = i.next();
1957 nc.add(ent.toHostNodeConnector());
1964 public Status addStaticHost(String networkAddress, String dataLayerAddress,
1965 NodeConnector nc, String vlan) {
1966 Long mac = HexEncode.stringToLong(dataLayerAddress);
1968 InetAddress addr = InetAddress.getByName(networkAddress);
1969 int ip = toIPv4Address(addr.getAddress());
1970 Entity e = new Entity(mac, Short.valueOf(vlan), ip, nc, new Date());
1972 if (switchManager.isNodeConnectorEnabled(e.getPort())) {
1973 Device d = this.learnDeviceByEntity(e);
1974 d.setStaticHost(true);
1977 "Switch or switchport is not up, adding host {} to inactive list",
1978 addr.getHostName());
1979 inactiveStaticDevices.put(e.getPort(), e);
1981 return new Status(StatusCode.SUCCESS);
1982 } catch (UnknownHostException e) {
1983 return new Status(StatusCode.INTERNALERROR);
1988 public Status removeStaticHost(String networkAddress) {
1991 addr = toIPv4Address(InetAddress.getByName(networkAddress)
1993 } catch (UnknownHostException e) {
1994 return new Status(StatusCode.NOTFOUND, "Host does not exist");
1996 Iterator<Device> di = this.getDeviceIteratorForQuery(null, null, addr,
1998 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
1999 while (di.hasNext()) {
2000 Device d = di.next();
2001 if (d.isStaticHost()) {
2003 for (IfNewHostNotify notify : newHostNotify) {
2004 notify.notifyHTClientHostRemoved(d.toHostNodeConnector());
2006 for (IDeviceListener listener : listeners) {
2007 listener.deviceRemoved(d);
2011 //go through inactive entites.
2012 Set<HostNodeConnector> inactive = this.getInactiveStaticHosts();
2013 for(HostNodeConnector nc : inactive){
2014 Integer ip =toIPv4Address(nc.getNetworkAddress().getAddress());
2015 if(ip.equals(addr)){
2016 this.inactiveStaticDevices.remove(nc.getnodeConnector());
2021 return new Status(StatusCode.SUCCESS);
2025 public void notifyNode(Node node, UpdateType type,
2026 Map<String, Property> propMap) {
2029 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2032 logger.debug("Received removed node {}", node);
2033 for (Entry<Long, Device> d : deviceMap.entrySet()) {
2034 Device device = d.getValue();
2035 HostNodeConnector host = device.toHostNodeConnector();
2036 if (host.getnodeconnectorNode().equals(node)) {
2037 logger.debug("Node: {} is down, remove from Hosts_DB", node);
2038 deleteDevice(device);
2039 for (IfNewHostNotify notify : newHostNotify) {
2040 notify.notifyHTClientHostRemoved(host);
2042 for (IDeviceListener listener : listeners) {
2043 listener.deviceRemoved(device);
2054 public void notifyNodeConnector(NodeConnector nodeConnector,
2055 UpdateType type, Map<String, Property> propMap) {
2056 if (nodeConnector == null)
2058 List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
2067 State state = (State) propMap.get(State.StatePropName);
2068 if ((state != null) && (state.getValue() == State.EDGE_UP)) {
2077 logger.debug("handleNodeConnectorStatusUp {}", nodeConnector);
2079 Entity ent = inactiveStaticDevices.get(nodeConnector);
2080 Device device = this.learnDeviceByEntity(ent);
2082 HostNodeConnector host = device.toHostNodeConnector();
2084 inactiveStaticDevices.remove(nodeConnector);
2085 for (IfNewHostNotify notify : newHostNotify) {
2086 notify.notifyHTClient(host);
2088 for (IDeviceListener listener : listeners) {
2089 listener.deviceAdded(device);
2092 logger.debug("handleNodeConnectorStatusDown {}", nodeConnector);
2096 // remove all devices on the node that went down.
2097 for (Entry<Long, Device> entry : deviceMap.entrySet()) {
2098 Device device = entry.getValue();
2099 HostNodeConnector host = device.toHostNodeConnector();
2100 if (host.getnodeConnector().equals(nodeConnector)) {
2101 deleteDevice(device);
2102 for (IfNewHostNotify notify : newHostNotify) {
2103 notify.notifyHTClientHostRemoved(host);
2105 for (IDeviceListener listener : listeners) {
2106 listener.deviceRemoved(device);