Add curly braces to if/else statements. (sonar)
[controller.git] / opendaylight / hosttracker_new / implementation / src / main / java / org / opendaylight / controller / hosttracker / internal / Device.java
1 /*
2  * Copyright (c) 2011,2012 Big Switch Networks, Inc.
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the
5  * "License"); you may not use this file except in compliance with the
6  * License. You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/legal/epl-v10.html
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13  * implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  *
16  * This file incorporates work covered by the following copyright and
17  * permission notice:
18  *
19  *    Originally created by David Erickson, Stanford University
20  *
21  *    Licensed under the Apache License, Version 2.0 (the "License");
22  *    you may not use this file except in compliance with the
23  *    License. You may obtain a copy of the License at
24  *
25  *         http://www.apache.org/licenses/LICENSE-2.0
26  *
27  *    Unless required by applicable law or agreed to in writing,
28  *    software distributed under the License is distributed on an "AS
29  *    IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
30  *    express or implied. See the License for the specific language
31  *    governing permissions and limitations under the License.
32  */
33
34 package org.opendaylight.controller.hosttracker.internal;
35
36 import java.net.InetAddress;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Date;
42 import java.util.EnumSet;
43 import java.util.HashMap;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.TreeSet;
48
49 import org.opendaylight.controller.hosttracker.Entity;
50 import org.opendaylight.controller.hosttracker.IDevice;
51 import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
52 import org.opendaylight.controller.hosttracker.IEntityClass;
53 import org.opendaylight.controller.hosttracker.SwitchPort;
54 import org.opendaylight.controller.hosttracker.SwitchPort.ErrorStatus;
55 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
56 import org.opendaylight.controller.sal.core.NodeConnector;
57 import org.opendaylight.controller.sal.utils.HexEncode;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * Concrete implementation of {@link IDevice}
63  *
64  * @author readams
65  */
66 public class Device implements IDevice {
67     protected static Logger log = LoggerFactory.getLogger(Device.class);
68     public static final short VLAN_UNTAGGED = (short) 0xffff;
69
70     private final Long deviceKey;
71     protected final DeviceManagerImpl deviceManager;
72
73     protected final Entity[] entities;
74     private final IEntityClass entityClass;
75
76     protected final String macAddressString;
77     // the vlan Ids from the entities of this device
78     protected final Short[] vlanIds;
79     protected volatile String dhcpClientName;
80
81     private boolean staticHost;
82
83     /**
84      * These are the old attachment points for the device that were valid no
85      * more than INACTIVITY_TIME ago.
86      */
87     protected volatile List<AttachmentPoint> oldAPs;
88     /**
89      * The current attachment points for the device.
90      */
91     protected volatile List<AttachmentPoint> attachmentPoints;
92
93     // ************
94     // Constructors
95     // ************
96
97     /**
98      * Create a device from an entities
99      *
100      * @param deviceManager
101      *            the device manager for this device
102      * @param deviceKey
103      *            the unique identifier for this device object
104      * @param entity
105      *            the initial entity for the device
106      * @param entityClass
107      *            the entity classes associated with the entity
108      */
109     public Device(DeviceManagerImpl deviceManager, Long deviceKey,
110             Entity entity, IEntityClass entityClass) {
111         this.deviceManager = deviceManager;
112         this.deviceKey = deviceKey;
113         this.entities = new Entity[] { entity };
114         this.macAddressString = HexEncode.longToHexString(entity
115                 .getMacAddress());
116         this.entityClass = entityClass;
117         Arrays.sort(this.entities);
118
119         this.dhcpClientName = null;
120         this.oldAPs = null;
121         this.attachmentPoints = null;
122
123         if (entity.getPort() != null) {
124             NodeConnector port = entity.getPort();
125
126             if (deviceManager.isValidAttachmentPoint(port)) {
127                 AttachmentPoint ap;
128                 ap = new AttachmentPoint(port, entity.getLastSeenTimestamp()
129                         .getTime());
130
131                 this.attachmentPoints = new ArrayList<AttachmentPoint>();
132                 this.attachmentPoints.add(ap);
133             }
134         }
135         vlanIds = computeVlandIds();
136     }
137
138     /**
139      * Create a device from a set of entities
140      *
141      * @param deviceManager
142      *            the device manager for this device
143      * @param deviceKey
144      *            the unique identifier for this device object
145      * @param entities
146      *            the initial entities for the device
147      * @param entityClass
148      *            the entity class associated with the entities
149      */
150     public Device(DeviceManagerImpl deviceManager, Long deviceKey,
151             String dhcpClientName, Collection<AttachmentPoint> oldAPs,
152             Collection<AttachmentPoint> attachmentPoints,
153             Collection<Entity> entities, IEntityClass entityClass) {
154         this.deviceManager = deviceManager;
155         this.deviceKey = deviceKey;
156         this.dhcpClientName = dhcpClientName;
157         this.entities = entities.toArray(new Entity[entities.size()]);
158         this.oldAPs = null;
159         this.attachmentPoints = null;
160         if (oldAPs != null) {
161             this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs);
162         }
163         if (attachmentPoints != null) {
164             this.attachmentPoints = new ArrayList<AttachmentPoint>(
165                     attachmentPoints);
166         }
167         this.macAddressString = HexEncode.longToHexString(this.entities[0]
168                 .getMacAddress());
169         this.entityClass = entityClass;
170         Arrays.sort(this.entities);
171         vlanIds = computeVlandIds();
172     }
173
174     /**
175      * Construct a new device consisting of the entities from the old device
176      * plus an additional entity. The caller needs to ensure that the additional
177      * entity is not already present in the array
178      *
179      * @param device
180      *            the old device object
181      * @param newEntity
182      *            the entity to add. newEntity must be have the same entity
183      *            class as device
184      * @param insertionpoint
185      *        if positive indicates the index in the entities array were the new
186      *        entity should be inserted. If negative we will compute the correct
187      *        insertion point
188      */
189     public Device(Device device, Entity newEntity, int insertionpoint) {
190         this.deviceManager = device.deviceManager;
191         this.deviceKey = device.deviceKey;
192         this.dhcpClientName = device.dhcpClientName;
193
194         this.entities = new Entity[device.entities.length + 1];
195         if (insertionpoint < 0) {
196             insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1);
197         }
198         if (insertionpoint > 0) {
199             // insertion point is not the beginning:
200             // copy up to insertion point
201             System.arraycopy(device.entities, 0, this.entities, 0,
202                     insertionpoint);
203         }
204         if (insertionpoint < device.entities.length) {
205             // insertion point is not the end
206             // copy from insertion point
207             System.arraycopy(device.entities, insertionpoint, this.entities,
208                     insertionpoint + 1, device.entities.length - insertionpoint);
209         }
210         this.entities[insertionpoint] = newEntity;
211         /*
212          * this.entities = Arrays.<Entity>copyOf(device.entities,
213          * device.entities.length + 1); this.entities[this.entities.length - 1]
214          * = newEntity; Arrays.sort(this.entities);
215          */
216         this.oldAPs = null;
217         if (device.oldAPs != null) {
218             this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs);
219         }
220         this.attachmentPoints = null;
221         if (device.attachmentPoints != null) {
222             this.attachmentPoints = new ArrayList<AttachmentPoint>(
223                     device.attachmentPoints);
224         }
225
226         this.macAddressString = HexEncode.longToHexString(this.entities[0]
227                 .getMacAddress());
228
229         this.entityClass = device.entityClass;
230         vlanIds = computeVlandIds();
231     }
232
233     private Short[] computeVlandIds() {
234         if (entities.length == 1) {
235             if (entities[0].getVlan() != null) {
236                 return new Short[] { entities[0].getVlan() };
237             } else {
238                 return new Short[] { Short.valueOf((short) -1) };
239             }
240         }
241
242         TreeSet<Short> vals = new TreeSet<Short>();
243         for (Entity e : entities) {
244             if (e.getVlan() == null) {
245                 vals.add((short) -1);
246             } else {
247                 vals.add(e.getVlan());
248             }
249         }
250         return vals.toArray(new Short[vals.size()]);
251     }
252
253     /**
254      * Given a list of attachment points (apList), the procedure would return a
255      * map of attachment points for each L2 domain. L2 domain id is the key.
256      *
257      * @param apList
258      * @return
259      */
260     private Map<Long, AttachmentPoint> getAPMap(List<AttachmentPoint> apList) {
261
262         if (apList == null)
263             return null;
264         // ITopologyService topology = deviceManager.topology;
265
266         // Get the old attachment points and sort them.
267         List<AttachmentPoint> oldAP = new ArrayList<AttachmentPoint>();
268         if (apList != null)
269             oldAP.addAll(apList);
270
271         // Remove invalid attachment points before sorting.
272         List<AttachmentPoint> tempAP = new ArrayList<AttachmentPoint>();
273         for (AttachmentPoint ap : oldAP) {
274             if (deviceManager.isValidAttachmentPoint(ap.getPort())) {
275                 tempAP.add(ap);
276             }
277         }
278         oldAP = tempAP;
279
280         Collections.sort(oldAP, deviceManager.apComparator);
281
282         // Map of attachment point by L2 domain Id.
283         Map<Long, AttachmentPoint> apMap = new HashMap<Long, AttachmentPoint>();
284
285         for (int i = 0; i < oldAP.size(); ++i) {
286             AttachmentPoint ap = oldAP.get(i);
287             // if this is not a valid attachment point, continue
288             if (!deviceManager.isValidAttachmentPoint(ap.getPort()))
289                 continue;
290
291             // long id = topology.getL2DomainId(ap.getSw());
292             // XXX - Missing functionality
293             long id = 0;
294
295             apMap.put(id, ap);
296         }
297
298         if (apMap.isEmpty())
299             return null;
300         return apMap;
301     }
302
303     /**
304      * Remove all attachment points that are older than INACTIVITY_INTERVAL from
305      * the list.
306      *
307      * @param apList
308      * @return
309      */
310     private boolean removeExpiredAttachmentPoints(List<AttachmentPoint> apList) {
311
312         List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>();
313
314         if (apList == null)
315             return false;
316
317         for (AttachmentPoint ap : apList) {
318             if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) {
319                expiredAPs.add(ap);
320             }
321         }
322         if (expiredAPs.size() > 0) {
323             apList.removeAll(expiredAPs);
324             return true;
325         } else {
326             return false;
327         }
328     }
329
330     /**
331      * Get a list of duplicate attachment points, given a list of old attachment
332      * points and one attachment point per L2 domain. Given a true attachment
333      * point in the L2 domain, say trueAP, another attachment point in the same
334      * L2 domain, say ap, is duplicate if: 1. ap is inconsistent with trueAP,
335      * and 2. active time of ap is after that of trueAP; and 3. last seen time
336      * of ap is within the last INACTIVITY_INTERVAL
337      *
338      * @param oldAPList
339      * @param apMap
340      * @return
341      */
342     List<AttachmentPoint> getDuplicateAttachmentPoints(
343             List<AttachmentPoint> oldAPList, Map<Long, AttachmentPoint> apMap) {
344         // ITopologyService topology = deviceManager.topology;
345         List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>();
346         long timeThreshold = System.currentTimeMillis()
347                 - AttachmentPoint.INACTIVITY_INTERVAL;
348
349         if (oldAPList == null || apMap == null)
350             return dupAPs;
351
352         for (AttachmentPoint ap : oldAPList) {
353             // XXX - Missing functionality
354             // long id = topology.getL2DomainId(ap.getSw());
355             long id = 0;
356             AttachmentPoint trueAP = apMap.get(id);
357
358             if (trueAP == null)
359                 continue;
360             // XXX - Missing functionality
361             // boolean c = (topology.isConsistent(trueAP.getSw(),
362             // trueAP.getPort(),
363             // ap.getSw(), ap.getPort()));
364             boolean c = true;
365             boolean active = (ap.getActiveSince() > trueAP.getActiveSince());
366             boolean last = ap.getLastSeen() > timeThreshold;
367             if (!c && active && last) {
368                 dupAPs.add(ap);
369             }
370         }
371
372         return dupAPs;
373     }
374
375     /**
376      * Update the known attachment points. This method is called whenever
377      * topology changes. The method returns true if there's any change to the
378      * list of attachment points -- which indicates a possible device move.
379      *
380      * @return
381      */
382     protected boolean updateAttachmentPoint() {
383         boolean moved = false;
384         this.oldAPs = attachmentPoints;
385         if (attachmentPoints == null || attachmentPoints.isEmpty())
386             return false;
387
388         List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
389         if (attachmentPoints != null)
390             apList.addAll(attachmentPoints);
391         Map<Long, AttachmentPoint> newMap = getAPMap(apList);
392         if (newMap == null || newMap.size() != apList.size()) {
393             moved = true;
394         }
395
396         // Prepare the new attachment point list.
397         if (moved) {
398             log.info("updateAttachmentPoint: ap {}  newmap {} ",
399                     attachmentPoints, newMap);
400             List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>();
401             if (newMap != null)
402                 newAPList.addAll(newMap.values());
403             this.attachmentPoints = newAPList;
404         }
405
406         // Set the oldAPs to null.
407         return moved;
408     }
409
410     /**
411      * Update the list of attachment points given that a new packet-in was seen
412      * from (sw, port) at time (lastSeen). The return value is true if there was
413      * any change to the list of attachment points for the device -- which
414      * indicates a device move.
415      *
416      * @param port
417      * @param lastSeen
418      * @return
419      */
420     protected boolean updateAttachmentPoint(NodeConnector port, long lastSeen) {
421         // ITopologyService topology = deviceManager.topology;
422         List<AttachmentPoint> oldAPList;
423         List<AttachmentPoint> apList;
424         boolean oldAPFlag = false;
425
426         if (!deviceManager.isValidAttachmentPoint(port))
427             return false;
428         AttachmentPoint newAP = new AttachmentPoint(port, lastSeen);
429         // Copy the oldAP and ap list.
430         apList = new ArrayList<AttachmentPoint>();
431         if (attachmentPoints != null)
432             apList.addAll(attachmentPoints);
433         oldAPList = new ArrayList<AttachmentPoint>();
434         if (oldAPs != null)
435             oldAPList.addAll(oldAPs);
436
437         // if the sw, port is in old AP, remove it from there
438         // and update the lastSeen in that object.
439         if (oldAPList.contains(newAP)) {
440             int index = oldAPList.indexOf(newAP);
441             newAP = oldAPList.remove(index);
442             newAP.setLastSeen(lastSeen);
443             this.oldAPs = oldAPList;
444             oldAPFlag = true;
445         }
446
447         // newAP now contains the new attachment point.
448
449         // Get the APMap is null or empty.
450         Map<Long, AttachmentPoint> apMap = getAPMap(apList);
451         if (apMap == null || apMap.isEmpty()) {
452             apList.add(newAP);
453             attachmentPoints = apList;
454             // there are no old attachment points - since the device exists,
455             // this
456             // may be because the host really moved (so the old AP port went
457             // down);
458             // or it may be because the switch restarted (so old APs were
459             // nullified).
460             // For now we will treat both cases as host moved.
461             return true;
462         }
463
464         // XXX - Missing functionality
465         // long id = topology.getL2DomainId(sw);
466         long id = 0;
467         AttachmentPoint oldAP = apMap.get(id);
468
469         if (oldAP == null) // No attachment on this L2 domain.
470         {
471             apList = new ArrayList<AttachmentPoint>();
472             apList.addAll(apMap.values());
473             apList.add(newAP);
474             this.attachmentPoints = apList;
475             return true; // new AP found on an L2 island.
476         }
477
478         // There is already a known attachment point on the same L2 island.
479         // we need to compare oldAP and newAP.
480         if (oldAP.equals(newAP)) {
481             // nothing to do here. just the last seen has to be changed.
482             if (newAP.lastSeen > oldAP.lastSeen) {
483                 oldAP.setLastSeen(newAP.lastSeen);
484             }
485             this.attachmentPoints = new ArrayList<AttachmentPoint>(
486                     apMap.values());
487             return false; // nothing to do here.
488         }
489
490         int x = deviceManager.apComparator.compare(oldAP, newAP);
491         if (x < 0) {
492             // newAP replaces oldAP.
493             apMap.put(id, newAP);
494             this.attachmentPoints = new ArrayList<AttachmentPoint>(
495                     apMap.values());
496
497             oldAPList = new ArrayList<AttachmentPoint>();
498             if (oldAPs != null)
499                 oldAPList.addAll(oldAPs);
500             oldAPList.add(oldAP);
501             this.oldAPs = oldAPList;
502             // XXX - Missing functionality
503             // if (!topology.isInSameBroadcastDomain(oldAP.getSw(),
504             // oldAP.getPort(),
505             // newAP.getSw(), newAP.getPort()))
506             // return true; // attachment point changed.
507             return true;
508         } else if (oldAPFlag) {
509             // retain oldAP as is. Put the newAP in oldAPs for flagging
510             // possible duplicates.
511             oldAPList = new ArrayList<AttachmentPoint>();
512             if (oldAPs != null)
513                 oldAPList.addAll(oldAPs);
514             // Add to oldAPList only if it was picked up from the oldAPList
515             oldAPList.add(newAP);
516             this.oldAPs = oldAPList;
517             // XXX - Missing functionality
518             // if (!topology.isInSameBroadcastDomain(oldAP.getSw(),
519             // oldAP.getPort(),
520             // newAP.getSw(), newAP.getPort()))
521             // return true; // attachment point changed.
522             return true;
523         }
524         return false;
525     }
526
527     /**
528      * Delete (sw,port) from the list of list of attachment points and oldAPs.
529      *
530      * @param port
531      * @return
532      */
533     public boolean deleteAttachmentPoint(NodeConnector port) {
534         AttachmentPoint ap = new AttachmentPoint(port, 0);
535
536         if (this.oldAPs != null) {
537             ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
538             apList.addAll(this.oldAPs);
539             int index = apList.indexOf(ap);
540             if (index > 0) {
541                 apList.remove(index);
542                 this.oldAPs = apList;
543             }
544         }
545
546         if (this.attachmentPoints != null) {
547             ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
548             apList.addAll(this.attachmentPoints);
549             int index = apList.indexOf(ap);
550             if (index > 0) {
551                 apList.remove(index);
552                 this.attachmentPoints = apList;
553                 return true;
554             }
555         }
556         return false;
557     }
558
559     // *******
560     // IDevice
561     // *******
562
563     @Override
564     public SwitchPort[] getOldAP() {
565         List<SwitchPort> sp = new ArrayList<SwitchPort>();
566         SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
567         if (oldAPs == null)
568             return returnSwitchPorts;
569         if (oldAPs.isEmpty())
570             return returnSwitchPorts;
571
572         // copy ap list.
573         List<AttachmentPoint> oldAPList;
574         oldAPList = new ArrayList<AttachmentPoint>();
575
576         if (oldAPs != null)
577             oldAPList.addAll(oldAPs);
578         removeExpiredAttachmentPoints(oldAPList);
579
580         if (oldAPList != null) {
581             for (AttachmentPoint ap : oldAPList) {
582                 SwitchPort swport = new SwitchPort(ap.getPort());
583                 sp.add(swport);
584             }
585         }
586         return sp.toArray(new SwitchPort[sp.size()]);
587     }
588
589     @Override
590     public SwitchPort[] getAttachmentPoints() {
591         return getAttachmentPoints(false);
592     }
593
594     @Override
595     public SwitchPort[] getAttachmentPoints(boolean includeError) {
596         List<SwitchPort> sp = new ArrayList<SwitchPort>();
597         SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
598         if (attachmentPoints == null)
599             return returnSwitchPorts;
600         if (attachmentPoints.isEmpty())
601             return returnSwitchPorts;
602
603         // copy ap list.
604         List<AttachmentPoint> apList = attachmentPoints;
605
606         if (apList != null) {
607             for (AttachmentPoint ap : apList) {
608                 SwitchPort swport = new SwitchPort(ap.getPort());
609                 sp.add(swport);
610             }
611         }
612
613         if (!includeError)
614             return sp.toArray(new SwitchPort[sp.size()]);
615
616         List<AttachmentPoint> oldAPList;
617         oldAPList = new ArrayList<AttachmentPoint>();
618
619         if (oldAPs != null)
620             oldAPList.addAll(oldAPs);
621
622         if (removeExpiredAttachmentPoints(oldAPList))
623             this.oldAPs = oldAPList;
624
625         List<AttachmentPoint> dupList;
626         // get AP map.
627         Map<Long, AttachmentPoint> apMap = getAPMap(apList);
628         dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
629         if (dupList != null) {
630             for (AttachmentPoint ap : dupList) {
631                 SwitchPort swport = new SwitchPort(ap.getPort(),
632                         ErrorStatus.DUPLICATE_DEVICE);
633                 sp.add(swport);
634             }
635         }
636         return sp.toArray(new SwitchPort[sp.size()]);
637     }
638
639     @Override
640     public Long getDeviceKey() {
641         return deviceKey;
642     }
643
644     @Override
645     public long getMACAddress() {
646         // we assume only one MAC per device for now.
647         return entities[0].getMacAddress();
648     }
649
650     @Override
651     public String getMACAddressString() {
652         return macAddressString;
653     }
654
655     @Override
656     public Short[] getVlanId() {
657         return Arrays.copyOf(vlanIds, vlanIds.length);
658     }
659
660     static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPV4);
661
662     @Override
663     public Integer[] getIPv4Addresses() {
664         // XXX - TODO we can cache this result. Let's find out if this
665         // is really a performance bottleneck first though.
666
667         TreeSet<Integer> vals = new TreeSet<Integer>();
668         for (Entity e : entities) {
669             if (e.getIpv4Address() == null)
670                 continue;
671
672             // We have an IP address only if among the devices within the class
673             // we have the most recent entity with that IP.
674             boolean validIP = true;
675             Iterator<Device> devices = deviceManager.queryClassByEntity(
676                     entityClass, ipv4Fields, e);
677             while (devices.hasNext()) {
678                 Device d = devices.next();
679                 if (deviceKey.equals(d.getDeviceKey()))
680                     continue;
681                 for (Entity se : d.entities) {
682                     if (se.getIpv4Address() != null
683                             && se.getIpv4Address().equals(e.getIpv4Address())
684                             && se.getLastSeenTimestamp() != null
685                             && 0 < se.getLastSeenTimestamp().compareTo(
686                                     e.getLastSeenTimestamp())) {
687                         validIP = false;
688                         break;
689                     }
690                 }
691                 if (!validIP)
692                     break;
693             }
694
695             if (validIP)
696                 vals.add(e.getIpv4Address());
697         }
698
699         return vals.toArray(new Integer[vals.size()]);
700     }
701
702     @Override
703     public Short[] getSwitchPortVlanIds(SwitchPort swp) {
704         TreeSet<Short> vals = new TreeSet<Short>();
705         for (Entity e : entities) {
706             if (e.getPort().equals(swp.getPort())) {
707                 if (e.getVlan() == null) {
708                     vals.add(VLAN_UNTAGGED);
709                 }
710                 else  {
711                     vals.add(e.getVlan());
712                 }
713             }
714         }
715         return vals.toArray(new Short[vals.size()]);
716     }
717
718     @Override
719     public Date getLastSeen() {
720         Date d = null;
721         for (int i = 0; i < entities.length; i++) {
722             if (d == null
723                     || entities[i].getLastSeenTimestamp().compareTo(d) > 0)
724                 d = entities[i].getLastSeenTimestamp();
725         }
726         return d;
727     }
728
729     // ***************
730     // Getters/Setters
731     // ***************
732
733     @Override
734     public IEntityClass getEntityClass() {
735         return entityClass;
736     }
737
738     public Entity[] getEntities() {
739         return entities;
740     }
741
742     public String getDHCPClientName() {
743         return dhcpClientName;
744     }
745
746     // ***************
747     // Utility Methods
748     // ***************
749
750     /**
751      * Check whether the device contains the specified entity
752      *
753      * @param entity
754      *            the entity to search for
755      * @return the index of the entity, or <0 if not found
756      */
757     protected int entityIndex(Entity entity) {
758         return Arrays.binarySearch(entities, entity);
759     }
760
761     // ******
762     // Object
763     // ******
764
765     @Override
766     public int hashCode() {
767         final int prime = 31;
768         int result = 1;
769         result = prime * result + Arrays.hashCode(entities);
770         return result;
771     }
772
773     @Override
774     public boolean equals(Object obj) {
775         if (this == obj)
776             return true;
777         if (obj == null)
778             return false;
779         if (getClass() != obj.getClass())
780             return false;
781         Device other = (Device) obj;
782         if (!deviceKey.equals(other.deviceKey))
783             return false;
784         if (!Arrays.equals(entities, other.entities))
785             return false;
786         return true;
787     }
788
789     public HostNodeConnector toHostNodeConnector() {
790         Integer[] ipv4s = this.getIPv4Addresses();
791         try {
792             Entity e = this.entities[this.entities.length-1];
793             NodeConnector n = null;
794             if(e!=null)
795                  n = e.getPort();
796             InetAddress ip = InetAddress.getByName(ipv4s[ipv4s.length - 1]
797                     .toString());
798             byte[] macAddr = macLongToByte(this.getMACAddress());
799             HostNodeConnector nc = new HostNodeConnector(macAddr, ip, n,
800                     (short) 0);
801             nc.setStaticHost(this.isStaticHost());
802             return nc;
803         } catch (Exception e) {
804             return null;
805         }
806     }
807
808     private byte[] macLongToByte(long mac) {
809         byte[] macAddr = new byte[6];
810         for (int i = 0; i < 6; i++) {
811             macAddr[5 - i] = (byte) (mac >> (8 * i));
812         }
813         return macAddr;
814     }
815
816     public boolean isStaticHost(){
817         return this.staticHost;
818     }
819
820     public void setStaticHost(boolean isStatic){
821         this.staticHost = isStatic;
822     }
823
824     @Override
825     public String toString() {
826         StringBuilder builder = new StringBuilder();
827         builder.append("Device [deviceKey=");
828         builder.append(deviceKey);
829         builder.append(", entityClass=");
830         builder.append(entityClass.getName());
831         builder.append(", MAC=");
832         builder.append(macAddressString);
833         builder.append(", IPs=[");
834         boolean isFirst = true;
835         for (Integer ip : getIPv4Addresses()) {
836             if (!isFirst)
837                 builder.append(", ");
838             isFirst = false;
839             // builder.append(IPv4.fromIPv4Address(ip));
840             builder.append(ip);
841         }
842         builder.append("], APs=");
843         builder.append(Arrays.toString(getAttachmentPoints(true)));
844         builder.append("]");
845         return builder.toString();
846     }
847 }