+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDevice;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.SwitchPort;
+import org.opendaylight.controller.hosttracker.SwitchPort.ErrorStatus;
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Concrete implementation of {@link IDevice}
+ *
+ * @author readams
+ */
+public class Device implements IDevice {
+ protected static Logger log = LoggerFactory.getLogger(Device.class);
+ public static final short VLAN_UNTAGGED = (short) 0xffff;
+
+ private final Long deviceKey;
+ protected final DeviceManagerImpl deviceManager;
+
+ protected final Entity[] entities;
+ private final IEntityClass entityClass;
+
+ protected final String macAddressString;
+ // the vlan Ids from the entities of this device
+ protected final Short[] vlanIds;
+ protected volatile String dhcpClientName;
+
+ private boolean staticHost;
+
+ /**
+ * These are the old attachment points for the device that were valid no
+ * more than INACTIVITY_TIME ago.
+ */
+ protected volatile List<AttachmentPoint> oldAPs;
+ /**
+ * The current attachment points for the device.
+ */
+ protected volatile List<AttachmentPoint> attachmentPoints;
+
+ // ************
+ // Constructors
+ // ************
+
+ /**
+ * Create a device from an entities
+ *
+ * @param deviceManager
+ * the device manager for this device
+ * @param deviceKey
+ * the unique identifier for this device object
+ * @param entity
+ * the initial entity for the device
+ * @param entityClass
+ * the entity classes associated with the entity
+ */
+ public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+ Entity entity, IEntityClass entityClass) {
+ this.deviceManager = deviceManager;
+ this.deviceKey = deviceKey;
+ this.entities = new Entity[] { entity };
+ this.macAddressString = HexEncode.longToHexString(entity
+ .getMacAddress());
+ this.entityClass = entityClass;
+ Arrays.sort(this.entities);
+
+ this.dhcpClientName = null;
+ this.oldAPs = null;
+ this.attachmentPoints = null;
+
+ if (entity.getPort() != null) {
+ NodeConnector port = entity.getPort();
+
+ if (deviceManager.isValidAttachmentPoint(port)) {
+ AttachmentPoint ap;
+ ap = new AttachmentPoint(port, entity.getLastSeenTimestamp()
+ .getTime());
+
+ this.attachmentPoints = new ArrayList<AttachmentPoint>();
+ this.attachmentPoints.add(ap);
+ }
+ }
+ vlanIds = computeVlandIds();
+ }
+
+ /**
+ * Create a device from a set of entities
+ *
+ * @param deviceManager
+ * the device manager for this device
+ * @param deviceKey
+ * the unique identifier for this device object
+ * @param entities
+ * the initial entities for the device
+ * @param entityClass
+ * the entity class associated with the entities
+ */
+ public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+ String dhcpClientName, Collection<AttachmentPoint> oldAPs,
+ Collection<AttachmentPoint> attachmentPoints,
+ Collection<Entity> entities, IEntityClass entityClass) {
+ this.deviceManager = deviceManager;
+ this.deviceKey = deviceKey;
+ this.dhcpClientName = dhcpClientName;
+ this.entities = entities.toArray(new Entity[entities.size()]);
+ this.oldAPs = null;
+ this.attachmentPoints = null;
+ if (oldAPs != null) {
+ this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs);
+ }
+ if (attachmentPoints != null) {
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ attachmentPoints);
+ }
+ this.macAddressString = HexEncode.longToHexString(this.entities[0]
+ .getMacAddress());
+ this.entityClass = entityClass;
+ Arrays.sort(this.entities);
+ vlanIds = computeVlandIds();
+ }
+
+ /**
+ * Construct a new device consisting of the entities from the old device
+ * plus an additional entity. The caller needs to ensure that the additional
+ * entity is not already present in the array
+ *
+ * @param device
+ * the old device object
+ * @param newEntity
+ * the entity to add. newEntity must be have the same entity
+ * class as device
+ * @param insertionpoint
+ * if positive indicates the index in the entities array were the new
+ * entity should be inserted. If negative we will compute the correct
+ * insertion point
+ */
+ public Device(Device device, Entity newEntity, int insertionpoint) {
+ this.deviceManager = device.deviceManager;
+ this.deviceKey = device.deviceKey;
+ this.dhcpClientName = device.dhcpClientName;
+
+ this.entities = new Entity[device.entities.length + 1];
+ if (insertionpoint < 0) {
+ insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1);
+ }
+ if (insertionpoint > 0) {
+ // insertion point is not the beginning:
+ // copy up to insertion point
+ System.arraycopy(device.entities, 0, this.entities, 0,
+ insertionpoint);
+ }
+ if (insertionpoint < device.entities.length) {
+ // insertion point is not the end
+ // copy from insertion point
+ System.arraycopy(device.entities, insertionpoint, this.entities,
+ insertionpoint + 1, device.entities.length - insertionpoint);
+ }
+ this.entities[insertionpoint] = newEntity;
+ /*
+ * this.entities = Arrays.<Entity>copyOf(device.entities,
+ * device.entities.length + 1); this.entities[this.entities.length - 1]
+ * = newEntity; Arrays.sort(this.entities);
+ */
+ this.oldAPs = null;
+ if (device.oldAPs != null) {
+ this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs);
+ }
+ this.attachmentPoints = null;
+ if (device.attachmentPoints != null) {
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ device.attachmentPoints);
+ }
+
+ this.macAddressString = HexEncode.longToHexString(this.entities[0]
+ .getMacAddress());
+
+ this.entityClass = device.entityClass;
+ vlanIds = computeVlandIds();
+ }
+
+ private Short[] computeVlandIds() {
+ if (entities.length == 1) {
+ if (entities[0].getVlan() != null) {
+ return new Short[] { entities[0].getVlan() };
+ } else {
+ return new Short[] { Short.valueOf((short) -1) };
+ }
+ }
+
+ TreeSet<Short> vals = new TreeSet<Short>();
+ for (Entity e : entities) {
+ if (e.getVlan() == null) {
+ vals.add((short) -1);
+ } else {
+ vals.add(e.getVlan());
+ }
+ }
+ return vals.toArray(new Short[vals.size()]);
+ }
+
+ /**
+ * Given a list of attachment points (apList), the procedure would return a
+ * map of attachment points for each L2 domain. L2 domain id is the key.
+ *
+ * @param apList
+ * @return
+ */
+ private Map<Long, AttachmentPoint> getAPMap(List<AttachmentPoint> apList) {
+
+ if (apList == null)
+ return null;
+ // ITopologyService topology = deviceManager.topology;
+
+ // Get the old attachment points and sort them.
+ List<AttachmentPoint> oldAP = new ArrayList<AttachmentPoint>();
+ if (apList != null)
+ oldAP.addAll(apList);
+
+ // Remove invalid attachment points before sorting.
+ List<AttachmentPoint> tempAP = new ArrayList<AttachmentPoint>();
+ for (AttachmentPoint ap : oldAP) {
+ if (deviceManager.isValidAttachmentPoint(ap.getPort())) {
+ tempAP.add(ap);
+ }
+ }
+ oldAP = tempAP;
+
+ Collections.sort(oldAP, deviceManager.apComparator);
+
+ // Map of attachment point by L2 domain Id.
+ Map<Long, AttachmentPoint> apMap = new HashMap<Long, AttachmentPoint>();
+
+ for (int i = 0; i < oldAP.size(); ++i) {
+ AttachmentPoint ap = oldAP.get(i);
+ // if this is not a valid attachment point, continue
+ if (!deviceManager.isValidAttachmentPoint(ap.getPort()))
+ continue;
+
+ // long id = topology.getL2DomainId(ap.getSw());
+ // XXX - Missing functionality
+ long id = 0;
+
+ apMap.put(id, ap);
+ }
+
+ if (apMap.isEmpty())
+ return null;
+ return apMap;
+ }
+
+ /**
+ * Remove all attachment points that are older than INACTIVITY_INTERVAL from
+ * the list.
+ *
+ * @param apList
+ * @return
+ */
+ private boolean removeExpiredAttachmentPoints(List<AttachmentPoint> apList) {
+
+ List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>();
+
+ if (apList == null)
+ return false;
+
+ for (AttachmentPoint ap : apList) {
+ if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) {
+ expiredAPs.add(ap);
+ }
+ }
+ if (expiredAPs.size() > 0) {
+ apList.removeAll(expiredAPs);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get a list of duplicate attachment points, given a list of old attachment
+ * points and one attachment point per L2 domain. Given a true attachment
+ * point in the L2 domain, say trueAP, another attachment point in the same
+ * L2 domain, say ap, is duplicate if: 1. ap is inconsistent with trueAP,
+ * and 2. active time of ap is after that of trueAP; and 3. last seen time
+ * of ap is within the last INACTIVITY_INTERVAL
+ *
+ * @param oldAPList
+ * @param apMap
+ * @return
+ */
+ List<AttachmentPoint> getDuplicateAttachmentPoints(
+ List<AttachmentPoint> oldAPList, Map<Long, AttachmentPoint> apMap) {
+ List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>();
+ long timeThreshold = System.currentTimeMillis()
+ - AttachmentPoint.INACTIVITY_INTERVAL;
+
+ if (oldAPList == null || apMap == null)
+ return dupAPs;
+
+ for (AttachmentPoint ap : oldAPList) {
+ long id = 0;
+ AttachmentPoint trueAP = apMap.get(id);
+
+ if (trueAP == null)
+ continue;
+ boolean c = true;
+ boolean active = (ap.getActiveSince() > trueAP.getActiveSince());
+ boolean last = ap.getLastSeen() > timeThreshold;
+ if (!c && active && last) {
+ dupAPs.add(ap);
+ }
+ }
+
+ return dupAPs;
+ }
+
+ /**
+ * Update the known attachment points. This method is called whenever
+ * topology changes. The method returns true if there's any change to the
+ * list of attachment points -- which indicates a possible device move.
+ *
+ * @return
+ */
+ protected boolean updateAttachmentPoint() {
+ boolean moved = false;
+ this.oldAPs = attachmentPoints;
+ if (attachmentPoints == null || attachmentPoints.isEmpty())
+ return false;
+
+ List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ if (attachmentPoints != null)
+ apList.addAll(attachmentPoints);
+ Map<Long, AttachmentPoint> newMap = getAPMap(apList);
+ if (newMap == null || newMap.size() != apList.size()) {
+ moved = true;
+ }
+
+ // Prepare the new attachment point list.
+ if (moved) {
+ log.info("updateAttachmentPoint: ap {} newmap {} ",
+ attachmentPoints, newMap);
+ List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>();
+ if (newMap != null)
+ newAPList.addAll(newMap.values());
+ this.attachmentPoints = newAPList;
+ }
+
+ // Set the oldAPs to null.
+ return moved;
+ }
+
+ /**
+ * Update the list of attachment points given that a new packet-in was seen
+ * from (sw, port) at time (lastSeen). The return value is true if there was
+ * any change to the list of attachment points for the device -- which
+ * indicates a device move.
+ *
+ * @param port
+ * @param lastSeen
+ * @return
+ */
+ protected boolean updateAttachmentPoint(NodeConnector port, long lastSeen) {
+ // ITopologyService topology = deviceManager.topology;
+ List<AttachmentPoint> oldAPList;
+ List<AttachmentPoint> apList;
+ boolean oldAPFlag = false;
+
+ if (!deviceManager.isValidAttachmentPoint(port))
+ return false;
+ AttachmentPoint newAP = new AttachmentPoint(port, lastSeen);
+ // Copy the oldAP and ap list.
+ apList = new ArrayList<AttachmentPoint>();
+ if (attachmentPoints != null)
+ apList.addAll(attachmentPoints);
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+
+ // if the sw, port is in old AP, remove it from there
+ // and update the lastSeen in that object.
+ if (oldAPList.contains(newAP)) {
+ int index = oldAPList.indexOf(newAP);
+ newAP = oldAPList.remove(index);
+ newAP.setLastSeen(lastSeen);
+ this.oldAPs = oldAPList;
+ oldAPFlag = true;
+ }
+
+ // newAP now contains the new attachment point.
+
+ // Get the APMap is null or empty.
+ Map<Long, AttachmentPoint> apMap = getAPMap(apList);
+ if (apMap == null || apMap.isEmpty()) {
+ apList.add(newAP);
+ attachmentPoints = apList;
+ // there are no old attachment points - since the device exists,
+ // this
+ // may be because the host really moved (so the old AP port went
+ // down);
+ // or it may be because the switch restarted (so old APs were
+ // nullified).
+ // For now we will treat both cases as host moved.
+ return true;
+ }
+
+ // XXX - Missing functionality
+ long id = 0;
+ AttachmentPoint oldAP = apMap.get(id);
+
+ if (oldAP == null) // No attachment on this L2 domain.
+ {
+ apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(apMap.values());
+ apList.add(newAP);
+ this.attachmentPoints = apList;
+ return true; // new AP found on an L2 island.
+ }
+
+ // There is already a known attachment point on the same L2 island.
+ // we need to compare oldAP and newAP.
+ if (oldAP.equals(newAP)) {
+ // nothing to do here. just the last seen has to be changed.
+ if (newAP.lastSeen > oldAP.lastSeen) {
+ oldAP.setLastSeen(newAP.lastSeen);
+ }
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ apMap.values());
+ return false; // nothing to do here.
+ }
+
+ int x = deviceManager.apComparator.compare(oldAP, newAP);
+ if (x < 0) {
+ // newAP replaces oldAP.
+ apMap.put(id, newAP);
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ apMap.values());
+
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ oldAPList.add(oldAP);
+ this.oldAPs = oldAPList;
+ return true;
+ } else if (oldAPFlag) {
+ // retain oldAP as is. Put the newAP in oldAPs for flagging
+ // possible duplicates.
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ // Add to oldAPList only if it was picked up from the oldAPList
+ oldAPList.add(newAP);
+ this.oldAPs = oldAPList;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Delete (sw,port) from the list of list of attachment points and oldAPs.
+ *
+ * @param port
+ * @return
+ */
+ public boolean deleteAttachmentPoint(NodeConnector port) {
+ AttachmentPoint ap = new AttachmentPoint(port, 0);
+
+ if (this.oldAPs != null) {
+ ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(this.oldAPs);
+ int index = apList.indexOf(ap);
+ if (index > 0) {
+ apList.remove(index);
+ this.oldAPs = apList;
+ }
+ }
+
+ if (this.attachmentPoints != null) {
+ ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(this.attachmentPoints);
+ int index = apList.indexOf(ap);
+ if (index > 0) {
+ apList.remove(index);
+ this.attachmentPoints = apList;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // *******
+ // IDevice
+ // *******
+
+ @Override
+ public SwitchPort[] getOldAP() {
+ List<SwitchPort> sp = new ArrayList<SwitchPort>();
+ SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+ if (oldAPs == null)
+ return returnSwitchPorts;
+ if (oldAPs.isEmpty())
+ return returnSwitchPorts;
+
+ // copy ap list.
+ List<AttachmentPoint> oldAPList;
+ oldAPList = new ArrayList<AttachmentPoint>();
+
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ removeExpiredAttachmentPoints(oldAPList);
+
+ if (oldAPList != null) {
+ for (AttachmentPoint ap : oldAPList) {
+ SwitchPort swport = new SwitchPort(ap.getPort());
+ sp.add(swport);
+ }
+ }
+ return sp.toArray(new SwitchPort[sp.size()]);
+ }
+
+ @Override
+ public SwitchPort[] getAttachmentPoints() {
+ return getAttachmentPoints(false);
+ }
+
+ @Override
+ public SwitchPort[] getAttachmentPoints(boolean includeError) {
+ List<SwitchPort> sp = new ArrayList<SwitchPort>();
+ SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+ if (attachmentPoints == null)
+ return returnSwitchPorts;
+ if (attachmentPoints.isEmpty())
+ return returnSwitchPorts;
+
+ // copy ap list.
+ List<AttachmentPoint> apList = attachmentPoints;
+
+ if (apList != null) {
+ for (AttachmentPoint ap : apList) {
+ SwitchPort swport = new SwitchPort(ap.getPort());
+ sp.add(swport);
+ }
+ }
+
+ if (!includeError)
+ return sp.toArray(new SwitchPort[sp.size()]);
+
+ List<AttachmentPoint> oldAPList;
+ oldAPList = new ArrayList<AttachmentPoint>();
+
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+
+ if (removeExpiredAttachmentPoints(oldAPList))
+ this.oldAPs = oldAPList;
+
+ List<AttachmentPoint> dupList;
+ // get AP map.
+ Map<Long, AttachmentPoint> apMap = getAPMap(apList);
+ dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
+ if (dupList != null) {
+ for (AttachmentPoint ap : dupList) {
+ SwitchPort swport = new SwitchPort(ap.getPort(),
+ ErrorStatus.DUPLICATE_DEVICE);
+ sp.add(swport);
+ }
+ }
+ return sp.toArray(new SwitchPort[sp.size()]);
+ }
+
+ @Override
+ public Long getDeviceKey() {
+ return deviceKey;
+ }
+
+ @Override
+ public long getMACAddress() {
+ // we assume only one MAC per device for now.
+ return entities[0].getMacAddress();
+ }
+
+ @Override
+ public String getMACAddressString() {
+ return macAddressString;
+ }
+
+ @Override
+ public Short[] getVlanId() {
+ return Arrays.copyOf(vlanIds, vlanIds.length);
+ }
+
+ static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPV4);
+
+ @Override
+ public Integer[] getIPv4Addresses() {
+ // XXX - TODO we can cache this result. Let's find out if this
+ // is really a performance bottleneck first though.
+
+ TreeSet<Integer> vals = new TreeSet<Integer>();
+ for (Entity e : entities) {
+ if (e.getIpv4Address() == null)
+ continue;
+
+ // We have an IP address only if among the devices within the class
+ // we have the most recent entity with that IP.
+ boolean validIP = true;
+ Iterator<Device> devices = deviceManager.queryClassByEntity(
+ entityClass, ipv4Fields, e);
+ while (devices.hasNext()) {
+ Device d = devices.next();
+ if (deviceKey.equals(d.getDeviceKey()))
+ continue;
+ for (Entity se : d.entities) {
+ if (se.getIpv4Address() != null
+ && se.getIpv4Address().equals(e.getIpv4Address())
+ && se.getLastSeenTimestamp() != null
+ && 0 < se.getLastSeenTimestamp().compareTo(
+ e.getLastSeenTimestamp())) {
+ validIP = false;
+ break;
+ }
+ }
+ if (!validIP)
+ break;
+ }
+
+ if (validIP)
+ vals.add(e.getIpv4Address());
+ }
+
+ return vals.toArray(new Integer[vals.size()]);
+ }
+
+ @Override
+ public Short[] getSwitchPortVlanIds(SwitchPort swp) {
+ TreeSet<Short> vals = new TreeSet<Short>();
+ for (Entity e : entities) {
+ if (e.getPort().equals(swp.getPort())) {
+ if (e.getVlan() == null) {
+ vals.add(VLAN_UNTAGGED);
+ }
+ else {
+ vals.add(e.getVlan());
+ }
+ }
+ }
+ return vals.toArray(new Short[vals.size()]);
+ }
+
+ @Override
+ public Date getLastSeen() {
+ Date d = null;
+ for (int i = 0; i < entities.length; i++) {
+ if (d == null
+ || entities[i].getLastSeenTimestamp().compareTo(d) > 0)
+ d = entities[i].getLastSeenTimestamp();
+ }
+ return d;
+ }
+
+ // ***************
+ // Getters/Setters
+ // ***************
+
+ @Override
+ public IEntityClass getEntityClass() {
+ return entityClass;
+ }
+
+ public Entity[] getEntities() {
+ return entities;
+ }
+
+ public String getDHCPClientName() {
+ return dhcpClientName;
+ }
+
+ // ***************
+ // Utility Methods
+ // ***************
+
+ /**
+ * Check whether the device contains the specified entity
+ *
+ * @param entity
+ * the entity to search for
+ * @return the index of the entity, or <0 if not found
+ */
+ protected int entityIndex(Entity entity) {
+ return Arrays.binarySearch(entities, entity);
+ }
+
+ // ******
+ // Object
+ // ******
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(entities);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Device other = (Device) obj;
+ if (!deviceKey.equals(other.deviceKey))
+ return false;
+ if (!Arrays.equals(entities, other.entities))
+ return false;
+ return true;
+ }
+
+ public HostNodeConnector toHostNodeConnector() {
+ Integer[] ipv4s = this.getIPv4Addresses();
+ try {
+ Entity e = this.entities[this.entities.length-1];
+ NodeConnector n = null;
+ if(e!=null)
+ n = e.getPort();
+ InetAddress ip = InetAddress.getByName(ipv4s[ipv4s.length - 1]
+ .toString());
+ byte[] macAddr = macLongToByte(this.getMACAddress());
+ HostNodeConnector nc = new HostNodeConnector(macAddr, ip, n,
+ (short) 0);
+ nc.setStaticHost(this.isStaticHost());
+ return nc;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private byte[] macLongToByte(long mac) {
+ byte[] macAddr = new byte[6];
+ for (int i = 0; i < 6; i++) {
+ macAddr[5 - i] = (byte) (mac >> (8 * i));
+ }
+ return macAddr;
+ }
+
+ public boolean isStaticHost(){
+ return this.staticHost;
+ }
+
+ public void setStaticHost(boolean isStatic){
+ this.staticHost = isStatic;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Device [deviceKey=");
+ builder.append(deviceKey);
+ builder.append(", entityClass=");
+ builder.append(entityClass.getName());
+ builder.append(", MAC=");
+ builder.append(macAddressString);
+ builder.append(", IPs=[");
+ boolean isFirst = true;
+ for (Integer ip : getIPv4Addresses()) {
+ if (!isFirst)
+ builder.append(", ");
+ isFirst = false;
+ builder.append(ip);
+ }
+ builder.append("], APs=");
+ builder.append(Arrays.toString(getAttachmentPoints(true)));
+ builder.append("]");
+ return builder.toString();
+ }
+}