Jonathan Hart

Add neighbor lifecycle management.

 * Made PIMNeighbor mostly immutable (apart from updatable timestamp)
 * Equals and hashCode for PIMNeighbor
 * Remove neighbor when we see a HELLO with holdTime==0
 * Periodic task to time out neighbors who haven't sent a HELLO in a while
 * Added a CLI command to view PIM neighbors

Change-Id: I59e52a847f7abcb8e9ac660c2cccace53e46867b
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
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 implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.pim.cli;
18 +
19 +import org.apache.karaf.shell.commands.Command;
20 +import org.onlab.util.Tools;
21 +import org.onosproject.cli.AbstractShellCommand;
22 +import org.onosproject.pim.impl.PIMInterface;
23 +import org.onosproject.pim.impl.PIMInterfaceService;
24 +import org.onosproject.pim.impl.PIMNeighbor;
25 +
26 +import java.util.Set;
27 +
28 +/**
29 + * Lists PIM neighbors.
30 + */
31 +@Command(scope = "onos", name = "pim-neighbors",
32 + description = "Lists the PIM neighbors")
33 +public class PimNeighborsListCommand extends AbstractShellCommand {
34 +
35 + private static final String INTF_FORMAT = "interface=%s, address=%s";
36 + private static final String NEIGHBOR_FORMAT = " neighbor=%s, uptime=%s, holdtime=%s, drPriority=%s, genId=%s";
37 +
38 + @Override
39 + protected void execute() {
40 + PIMInterfaceService interfaceService = get(PIMInterfaceService.class);
41 +
42 + Set<PIMInterface> interfaces = interfaceService.getPimInterfaces();
43 +
44 + for (PIMInterface intf : interfaces) {
45 + print(INTF_FORMAT, intf.getInterface().name(), intf.getIpAddress());
46 + for (PIMNeighbor neighbor : intf.getNeighbors()) {
47 + // Filter out the PIM neighbor representing 'us'
48 + if (!neighbor.ipAddress().equals(intf.getIpAddress())) {
49 + print(NEIGHBOR_FORMAT, neighbor.ipAddress(),
50 + Tools.timeAgo(neighbor.upTime()), neighbor.holdtime(),
51 + neighbor.priority(), neighbor.generationId());
52 + }
53 + }
54 + }
55 + }
56 +
57 +}
...@@ -32,10 +32,12 @@ import org.onosproject.net.packet.PacketService; ...@@ -32,10 +32,12 @@ import org.onosproject.net.packet.PacketService;
32 import org.slf4j.Logger; 32 import org.slf4j.Logger;
33 33
34 import java.nio.ByteBuffer; 34 import java.nio.ByteBuffer;
35 +import java.util.Collection;
35 import java.util.HashMap; 36 import java.util.HashMap;
36 import java.util.Map; 37 import java.util.Map;
37 import java.util.Random; 38 import java.util.Random;
38 import java.util.Set; 39 import java.util.Set;
40 +import java.util.stream.Collectors;
39 41
40 import static com.google.common.base.Preconditions.checkArgument; 42 import static com.google.common.base.Preconditions.checkArgument;
41 import static com.google.common.base.Preconditions.checkNotNull; 43 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -99,10 +101,7 @@ public final class PIMInterface { ...@@ -99,10 +101,7 @@ public final class PIMInterface {
99 generationId = new Random().nextInt(); 101 generationId = new Random().nextInt();
100 102
101 // Create a PIM Neighbor to represent ourselves for DR election. 103 // Create a PIM Neighbor to represent ourselves for DR election.
102 - PIMNeighbor us = new PIMNeighbor(ourIp, mac); 104 + PIMNeighbor us = new PIMNeighbor(ourIp, mac, holdTime, 0, priority, generationId);
103 -
104 - // Priority and IP address are all we need to DR election.
105 - us.setPriority(priority);
106 105
107 pimNeighbors.put(ourIp, us); 106 pimNeighbors.put(ourIp, us);
108 drIpaddress = ourIp; 107 drIpaddress = ourIp;
...@@ -199,6 +198,32 @@ public final class PIMInterface { ...@@ -199,6 +198,32 @@ public final class PIMInterface {
199 } 198 }
200 199
201 /** 200 /**
201 + * Gets the neighbors seen on this interface.
202 + *
203 + * @return PIM neighbors
204 + */
205 + public Collection<PIMNeighbor> getNeighbors() {
206 + return pimNeighbors.values();
207 + }
208 +
209 + /**
210 + * Checks whether any of our neighbors have expired, and cleans up their
211 + * state if they have.
212 + */
213 + public void checkNeighborTimeouts() {
214 + Set<PIMNeighbor> expired = pimNeighbors.values().stream()
215 + // Don't time ourselves out!
216 + .filter(neighbor -> !neighbor.ipAddress().equals(getIpAddress()))
217 + .filter(neighbor -> neighbor.isExpired())
218 + .collect(Collectors.toSet());
219 +
220 + for (PIMNeighbor neighbor : expired) {
221 + log.info("Timing out neighbor {}", neighbor);
222 + pimNeighbors.remove(neighbor.ipAddress(), neighbor);
223 + }
224 + }
225 +
226 + /**
202 * Multicast a hello message out our interface. This hello message is sent 227 * Multicast a hello message out our interface. This hello message is sent
203 * periodically during the normal PIM Neighbor refresh time, as well as a 228 * periodically during the normal PIM Neighbor refresh time, as well as a
204 * result of a newly created interface. 229 * result of a newly created interface.
...@@ -234,7 +259,7 @@ public final class PIMInterface { ...@@ -234,7 +259,7 @@ public final class PIMInterface {
234 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li> 259 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li>
235 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li> 260 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li>
236 * <li>We <em>may</em> need to send an existing neighbor all joins if the genid changed</li> 261 * <li>We <em>may</em> need to send an existing neighbor all joins if the genid changed</li>
237 - * <li>We will refresh the neighbors timestamp</li> 262 + * <li>We will refresh the neighbor's timestamp</li>
238 * </ul> 263 * </ul>
239 * 264 *
240 * @param ethPkt the Ethernet packet header 265 * @param ethPkt the Ethernet packet header
...@@ -259,7 +284,7 @@ public final class PIMInterface { ...@@ -259,7 +284,7 @@ public final class PIMInterface {
259 checkNotNull(dr); 284 checkNotNull(dr);
260 285
261 IpAddress drip = drIpaddress; 286 IpAddress drip = drIpaddress;
262 - int drpri = dr.getPriority(); 287 + int drpri = dr.priority();
263 288
264 // Assume we do not need to run a DR election 289 // Assume we do not need to run a DR election
265 boolean reElectDr = false; 290 boolean reElectDr = false;
...@@ -269,18 +294,24 @@ public final class PIMInterface { ...@@ -269,18 +294,24 @@ public final class PIMInterface {
269 294
270 // Determine if we already have a PIMNeighbor 295 // Determine if we already have a PIMNeighbor
271 PIMNeighbor nbr = pimNeighbors.getOrDefault(srcip, null); 296 PIMNeighbor nbr = pimNeighbors.getOrDefault(srcip, null);
297 + PIMNeighbor newNbr = PIMNeighbor.createPimNeighbor(srcip, nbrmac, hello.getOptions().values());
298 +
272 if (nbr == null) { 299 if (nbr == null) {
273 - nbr = new PIMNeighbor(srcip, hello.getOptions()); 300 + pimNeighbors.putIfAbsent(srcip, newNbr);
274 - checkNotNull(nbr); 301 + nbr = newNbr;
275 - } else { 302 + } else if (!nbr.equals(newNbr)) {
276 - Integer previousGenid = nbr.getGenid(); 303 + if (newNbr.holdtime() == 0) {
277 - nbr.addOptions(hello.getOptions()); 304 + // Neighbor has shut down. Remove them and clean up
278 - if (previousGenid != nbr.getGenid()) { 305 + pimNeighbors.remove(srcip, nbr);
279 - genidChanged = true; 306 + return;
307 + } else {
308 + // Neighbor has changed one of their options.
309 + pimNeighbors.put(srcip, newNbr);
310 + nbr = newNbr;
280 } 311 }
281 } 312 }
282 313
283 - // Refresh this neighbors timestamp 314 + // Refresh this neighbor's timestamp
284 nbr.refreshTimestamp(); 315 nbr.refreshTimestamp();
285 316
286 /* 317 /*
...@@ -300,8 +331,8 @@ public final class PIMInterface { ...@@ -300,8 +331,8 @@ public final class PIMInterface {
300 // Run an election if we need to. Return the elected IP address. 331 // Run an election if we need to. Return the elected IP address.
301 private IpAddress election(PIMNeighbor nbr, IpAddress drIp, int drPriority) { 332 private IpAddress election(PIMNeighbor nbr, IpAddress drIp, int drPriority) {
302 333
303 - IpAddress nbrIp = nbr.getIpaddr(); 334 + IpAddress nbrIp = nbr.ipAddress();
304 - if (nbr.getPriority() > drPriority) { 335 + if (nbr.priority() > drPriority) {
305 return nbrIp; 336 return nbrIp;
306 } 337 }
307 338
......
...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference; 23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality; 24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.apache.felix.scr.annotations.Service; 25 import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.util.SafeRecurringTask;
26 import org.onosproject.incubator.net.intf.Interface; 27 import org.onosproject.incubator.net.intf.Interface;
27 import org.onosproject.incubator.net.intf.InterfaceEvent; 28 import org.onosproject.incubator.net.intf.InterfaceEvent;
28 import org.onosproject.incubator.net.intf.InterfaceListener; 29 import org.onosproject.incubator.net.intf.InterfaceListener;
...@@ -58,8 +59,10 @@ public class PIMInterfaceManager implements PIMInterfaceService { ...@@ -58,8 +59,10 @@ public class PIMInterfaceManager implements PIMInterfaceService {
58 private static final Class<PimInterfaceConfig> PIM_INTERFACE_CONFIG_CLASS = PimInterfaceConfig.class; 59 private static final Class<PimInterfaceConfig> PIM_INTERFACE_CONFIG_CLASS = PimInterfaceConfig.class;
59 private static final String PIM_INTERFACE_CONFIG_KEY = "pimInterface"; 60 private static final String PIM_INTERFACE_CONFIG_KEY = "pimInterface";
60 61
61 - // Create a Scheduled Executor service to send PIM hellos 62 + private static final int DEFAULT_TIMEOUT_TASK_PERIOD_MS = 250;
62 - private final ScheduledExecutorService helloScheduler = 63 +
64 + // Create a Scheduled Executor service for recurring tasks
65 + private final ScheduledExecutorService scheduledExecutorService =
63 Executors.newScheduledThreadPool(1); 66 Executors.newScheduledThreadPool(1);
64 67
65 // Wait for a bout 3 seconds before sending the initial hello messages. 68 // Wait for a bout 3 seconds before sending the initial hello messages.
...@@ -69,6 +72,8 @@ public class PIMInterfaceManager implements PIMInterfaceService { ...@@ -69,6 +72,8 @@ public class PIMInterfaceManager implements PIMInterfaceService {
69 // Send PIM hello packets: 30 seconds. 72 // Send PIM hello packets: 30 seconds.
70 private final long pimHelloPeriod = 30; 73 private final long pimHelloPeriod = 30;
71 74
75 + private final int timeoutTaskPeriod = DEFAULT_TIMEOUT_TASK_PERIOD_MS;
76 +
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected PacketService packetService; 78 protected PacketService packetService;
74 79
...@@ -113,18 +118,16 @@ public class PIMInterfaceManager implements PIMInterfaceService { ...@@ -113,18 +118,16 @@ public class PIMInterfaceManager implements PIMInterfaceService {
113 interfaceService.addListener(interfaceListener); 118 interfaceService.addListener(interfaceListener);
114 119
115 // Schedule the periodic hello sender. 120 // Schedule the periodic hello sender.
116 - helloScheduler.scheduleAtFixedRate(new Runnable() { 121 + scheduledExecutorService.scheduleAtFixedRate(
117 - @Override 122 + SafeRecurringTask.wrap(
118 - public void run() { 123 + () -> pimInterfaces.values().forEach(PIMInterface::sendHello)),
119 - try { 124 + initialHelloDelay, pimHelloPeriod, TimeUnit.SECONDS);
120 - for (PIMInterface pif : pimInterfaces.values()) { 125 +
121 - pif.sendHello(); 126 + // Schedule task to periodically time out expired neighbors
122 - } 127 + scheduledExecutorService.scheduleAtFixedRate(
123 - } catch (Exception e) { 128 + SafeRecurringTask.wrap(
124 - log.warn("exception", e); 129 + () -> pimInterfaces.values().forEach(PIMInterface::checkNeighborTimeouts)),
125 - } 130 + 0, timeoutTaskPeriod, TimeUnit.MILLISECONDS);
126 - }
127 - }, initialHelloDelay, pimHelloPeriod, TimeUnit.SECONDS);
128 131
129 log.info("Started"); 132 log.info("Started");
130 } 133 }
...@@ -136,7 +139,7 @@ public class PIMInterfaceManager implements PIMInterfaceService { ...@@ -136,7 +139,7 @@ public class PIMInterfaceManager implements PIMInterfaceService {
136 networkConfig.unregisterConfigFactory(pimConfigFactory); 139 networkConfig.unregisterConfigFactory(pimConfigFactory);
137 140
138 // Shutdown the periodic hello task. 141 // Shutdown the periodic hello task.
139 - helloScheduler.shutdown(); 142 + scheduledExecutorService.shutdown();
140 143
141 log.info("Stopped"); 144 log.info("Stopped");
142 } 145 }
......
...@@ -15,216 +15,226 @@ ...@@ -15,216 +15,226 @@
15 */ 15 */
16 package org.onosproject.pim.impl; 16 package org.onosproject.pim.impl;
17 17
18 +import com.google.common.base.MoreObjects;
18 import org.onlab.packet.IpAddress; 19 import org.onlab.packet.IpAddress;
19 import org.onlab.packet.MacAddress; 20 import org.onlab.packet.MacAddress;
20 import org.onlab.packet.pim.PIMHelloOption; 21 import org.onlab.packet.pim.PIMHelloOption;
21 -import org.slf4j.Logger;
22 22
23 import java.nio.ByteBuffer; 23 import java.nio.ByteBuffer;
24 -import java.util.Calendar; 24 +import java.util.Collection;
25 -import java.util.Date; 25 +import java.util.Objects;
26 -import java.util.Map; 26 +import java.util.concurrent.TimeUnit;
27 27
28 -import static org.slf4j.LoggerFactory.getLogger; 28 +import static com.google.common.base.Preconditions.checkNotNull;
29 29
30 +/**
31 + * Represents a PIM neighbor.
32 + */
30 public class PIMNeighbor { 33 public class PIMNeighbor {
31 34
32 - private final Logger log = getLogger(getClass());
33 -
34 // IP Address of this neighbor 35 // IP Address of this neighbor
35 - private IpAddress ipAddr; 36 + private final IpAddress ipAddr;
36 37
37 // MAC Address of the neighbor (Need for sending J/P) 38 // MAC Address of the neighbor (Need for sending J/P)
38 - private MacAddress macAddr; 39 + private final MacAddress macAddr;
39 40
40 // Hello Options 41 // Hello Options
41 // Our hello opt holdTime 42 // Our hello opt holdTime
42 - private short holdTime; 43 + private final short holdTime;
43 44
44 // Our hello opt prune delay 45 // Our hello opt prune delay
45 - private int pruneDelay; 46 + private final int pruneDelay;
46 47
47 // Neighbor priority 48 // Neighbor priority
48 - private int priority; 49 + private final int priority;
49 50
50 // Our current genId 51 // Our current genId
51 - private int genId; 52 + private final int genId;
53 +
54 + private final long upTime;
52 55
53 // Our timestamp for this neighbor 56 // Our timestamp for this neighbor
54 - private Date lastRefresh; 57 + private long lastRefresh;
55 58
56 /** 59 /**
57 - * Construct a new PIM Neighbor. 60 + * Class constructor.
58 * 61 *
59 - * @param ipAddr the IP Address of our new neighbor 62 + * @param ipAddress neighbor IP address
60 - * @param opts option map 63 + * @param macAddress neighbor MAC address
64 + * @param holdTime hold time
65 + * @param pruneDelay prune delay
66 + * @param priority priority
67 + * @param genId generation ID
61 */ 68 */
62 - public PIMNeighbor(IpAddress ipAddr, Map<Short, PIMHelloOption> opts) { 69 + public PIMNeighbor(IpAddress ipAddress, MacAddress macAddress,
63 - this.ipAddr = ipAddr; 70 + short holdTime, int pruneDelay, int priority, int genId) {
64 - this.addOptions(opts); 71 + this.ipAddr = checkNotNull(ipAddress);
72 + this.macAddr = checkNotNull(macAddress);
73 + this.holdTime = holdTime;
74 + this.pruneDelay = pruneDelay;
75 + this.priority = priority;
76 + this.genId = genId;
77 +
78 + this.upTime = System.currentTimeMillis();
65 } 79 }
66 80
67 /** 81 /**
68 - * Construct a new PIM neighbor. 82 + * Gets the IP address of our neighbor.
69 * 83 *
70 - * @param ipAddr the neighbors IP addr 84 + * @return the IP address of our neighbor
71 - * @param macAddr MAC address
72 */ 85 */
73 - public PIMNeighbor(IpAddress ipAddr, MacAddress macAddr) { 86 + public IpAddress ipAddress() {
74 - this.ipAddr = ipAddr; 87 + return ipAddr;
75 - this.macAddr = macAddr;
76 } 88 }
77 89
78 /** 90 /**
79 - * Get the MAC address of this neighbor. 91 + * Gets the MAC address of this neighbor.
80 * 92 *
81 * @return the mac address 93 * @return the mac address
82 */ 94 */
83 - public MacAddress getMacaddr() { 95 + public MacAddress macAddress() {
84 return macAddr; 96 return macAddr;
85 } 97 }
86 98
87 /** 99 /**
88 - * Get the IP Address of our neighbor. 100 + * Gets our neighbor's hold time.
89 * 101 *
90 - * @return the IP address of our neighbor 102 + * @return the hold time
91 */ 103 */
92 - public IpAddress getIpaddr() { 104 + public short holdtime() {
93 - return ipAddr; 105 + return holdTime;
94 } 106 }
95 107
96 /** 108 /**
97 - * Set the IP address of our neighbor. 109 + * Gets our neighbor's prune delay.
98 * 110 *
99 - * @param ipAddr our neighbors IP address 111 + * @return our neighbor's prune delay
100 */ 112 */
101 - public void setIpaddr(IpAddress ipAddr) { 113 + public int pruneDelay() {
102 - this.ipAddr = ipAddr; 114 + return pruneDelay;
103 } 115 }
104 116
105 /** 117 /**
106 - * Get our neighbors holdTime. 118 + * Gets our neighbor's priority.
107 * 119 *
108 - * @return the holdTime 120 + * @return our neighbor's priority
109 */ 121 */
110 - public short getHoldtime() { 122 + public int priority() {
111 - return holdTime; 123 + return priority;
112 } 124 }
113 125
114 /** 126 /**
115 - * Set our neighbors holdTime. 127 + * Gets our neighbor's generation ID.
116 * 128 *
117 - * @param holdTime the holdTime 129 + * @return our neighbor's generation ID
118 */ 130 */
119 - public void setHoldtime(short holdTime) { 131 + public int generationId() {
120 - this.holdTime = holdTime; 132 + return genId;
121 } 133 }
122 134
123 /** 135 /**
124 - * Get our neighbors prune delay. 136 + * Gets the last time we heard a HELLO from this neighbor.
125 * 137 *
126 - * @return our neighbors prune delay 138 + * @return last refresh time
127 */ 139 */
128 - public int getPruneDelay() { 140 + public long lastRefresh() {
129 - return pruneDelay; 141 + return lastRefresh;
130 } 142 }
131 143
132 /** 144 /**
133 - * Set our neighbors prune delay. 145 + * Gets the time that we first learnt of this neighbor.
134 * 146 *
135 - * @param pruneDelay the prune delay 147 + * @return up time
136 */ 148 */
137 - public void setPruneDelay(int pruneDelay) { 149 + public long upTime() {
138 - this.pruneDelay = pruneDelay; 150 + return upTime;
139 } 151 }
140 152
141 /** 153 /**
142 - * Get our neighbors priority. 154 + * Refreshes this neighbor's last seen timestamp.
143 - *
144 - * @return our neighbors priority
145 */ 155 */
146 - public int getPriority() { 156 + public void refreshTimestamp() {
147 - return priority; 157 + lastRefresh = System.currentTimeMillis();
148 } 158 }
149 159
150 /** 160 /**
151 - * Set our neighbors priority. 161 + * Returns whether this neighbor is expired or not.
152 * 162 *
153 - * @param priority our neighbors priority 163 + * @return true if the neighbor is expired, otherwise false
154 */ 164 */
155 - public void setPriority(int priority) { 165 + public boolean isExpired() {
156 - this.priority = priority; 166 + return lastRefresh + TimeUnit.SECONDS.toMillis(holdTime)
167 + < System.currentTimeMillis();
157 } 168 }
158 169
159 /** 170 /**
160 - * Get our neighbors Genid. 171 + * Creates a PIM neighbor based on an IP, MAC, and collection of PIM HELLO
172 + * options.
161 * 173 *
162 - * @return our neighbor Genid 174 + * @param ipAddress neighbor IP address
175 + * @param macAddress neighbor MAC address
176 + * @param opts options from the PIM HELLO packet
177 + * @return new PIM neighbor
163 */ 178 */
164 - public int getGenid() { 179 + public static PIMNeighbor createPimNeighbor(IpAddress ipAddress,
165 - return genId; 180 + MacAddress macAddress,
166 - } 181 + Collection<PIMHelloOption> opts) {
182 +
183 + int generationID = PIMHelloOption.DEFAULT_GENID;
184 + short holdTime = PIMHelloOption.DEFAULT_HOLDTIME;
185 + int priority = PIMHelloOption.DEFAULT_PRIORITY;
186 + int pruneDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
187 +
188 + for (PIMHelloOption opt : opts) {
189 + short type = opt.getOptType();
190 + ByteBuffer value = ByteBuffer.wrap(opt.getValue());
191 +
192 + if (type == PIMHelloOption.OPT_GENID) {
193 + generationID = value.getInt();
194 + } else if (type == PIMHelloOption.OPT_HOLDTIME) {
195 + holdTime = value.getShort();
196 + } else if (type == PIMHelloOption.OPT_PRIORITY) {
197 + priority = value.getInt();
198 + } else if (type == PIMHelloOption.OPT_PRUNEDELAY) {
199 + pruneDelay = value.getInt();
200 + } else if (type == PIMHelloOption.OPT_ADDRLIST) {
201 + // TODO: Will implement someday
202 + }
203 + }
167 204
168 - /** 205 + return new PIMNeighbor(ipAddress, macAddress, holdTime, pruneDelay, priority, generationID);
169 - * Set our neighbors GenId.
170 - *
171 - * @param genId our neighbors GenId
172 - */
173 - public void setGenid(int genId) {
174 - this.genId = genId;
175 } 206 }
176 207
177 - /** 208 + @Override
178 - * Add the options for this neighbor if needed. 209 + public boolean equals(Object other) {
179 - * 210 + if (!(other instanceof PIMNeighbor)) {
180 - * @param opts the options to be added/modified 211 + return false;
181 - * @return true if options changed, false if no option has changed 212 + }
182 - */
183 - public boolean addOptions(Map<Short, PIMHelloOption> opts) {
184 213
185 - boolean changed = false; 214 + PIMNeighbor that = (PIMNeighbor) other;
186 215
187 - for (PIMHelloOption opt : opts.values()) { 216 + return this.ipAddr.equals(that.ipAddress()) &&
188 - Short otype = opt.getOptType(); 217 + this.macAddr.equals(that.macAddress()) &&
189 - ByteBuffer val = ByteBuffer.wrap(opt.getValue()); 218 + this.genId == that.genId &&
219 + this.holdTime == that.holdTime &&
220 + this.priority == that.priority;
221 + }
190 222
191 - if (otype == PIMHelloOption.OPT_ADDRLIST) { 223 + @Override
192 - // TODO: Will implement someday 224 + public int hashCode() {
193 - } else if (otype == PIMHelloOption.OPT_GENID) { 225 + return Objects.hash(ipAddr, macAddr, genId, holdTime, priority);
194 - int newval = val.getInt();
195 - if (newval != genId) {
196 - genId = newval;
197 - changed = true;
198 - }
199 - } else if (otype == PIMHelloOption.OPT_HOLDTIME) {
200 - short newval = val.getShort();
201 - if (newval != holdTime) {
202 - holdTime = newval;
203 - changed = true;
204 - }
205 - } else if (otype == PIMHelloOption.OPT_PRIORITY) {
206 - int newval = val.getInt();
207 - if (newval != priority) {
208 - priority = newval;
209 - changed = true;
210 - }
211 - } else if (otype == PIMHelloOption.OPT_PRUNEDELAY) {
212 - int newval = val.getInt();
213 - if (newval != pruneDelay) {
214 - pruneDelay = newval;
215 - changed = true;
216 - }
217 - } else {
218 - log.warn("received unknown pim hello options" + otype);
219 - }
220 - }
221 - return changed;
222 } 226 }
223 227
224 - /** 228 + @Override
225 - * Refresh this neighbors timestamp. 229 + public String toString() {
226 - */ 230 + return MoreObjects.toStringHelper(getClass())
227 - public void refreshTimestamp() { 231 + .add("ipAddress", ipAddr)
228 - lastRefresh = Calendar.getInstance().getTime(); 232 + .add("macAddress", macAddr)
233 + .add("generationId", genId)
234 + .add("holdTime", holdTime)
235 + .add("priority", priority)
236 + .add("pruneDelay", pruneDelay)
237 + .toString();
229 } 238 }
239 +
230 } 240 }
......
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
19 <command> 19 <command>
20 <action class="org.onosproject.pim.cli.PimInterfacesListCommand"/> 20 <action class="org.onosproject.pim.cli.PimInterfacesListCommand"/>
21 </command> 21 </command>
22 + <command>
23 + <action class="org.onosproject.pim.cli.PimNeighborsListCommand"/>
24 + </command>
22 </command-bundle> 25 </command-bundle>
23 26
24 </blueprint> 27 </blueprint>
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
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 implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onlab.util;
18 +
19 +import org.slf4j.Logger;
20 +import org.slf4j.LoggerFactory;
21 +
22 +/**
23 + * Wrapper for a recurring task which catches all exceptions to prevent task
24 + * being suppressed in a ScheduledExecutorService.
25 + */
26 +public final class SafeRecurringTask implements Runnable {
27 +
28 + private static final Logger log = LoggerFactory.getLogger(SafeRecurringTask.class);
29 +
30 + private final Runnable runnable;
31 +
32 + /**
33 + * Constructor.
34 + *
35 + * @param runnable runnable to wrap
36 + */
37 + private SafeRecurringTask(Runnable runnable) {
38 + this.runnable = runnable;
39 + }
40 +
41 + @Override
42 + public void run() {
43 + if (Thread.currentThread().isInterrupted()) {
44 + log.info("Task interrupted, quitting");
45 + return;
46 + }
47 +
48 + try {
49 + runnable.run();
50 + } catch (Exception e) {
51 + // Catch all exceptions to avoid task being suppressed
52 + log.error("Exception thrown during task", e);
53 + }
54 + }
55 +
56 + /**
57 + * Wraps a runnable in a safe recurring task.
58 + *
59 + * @param runnable runnable to wrap
60 + * @return safe recurring task
61 + */
62 + public static SafeRecurringTask wrap(Runnable runnable) {
63 + return new SafeRecurringTask(runnable);
64 + }
65 +
66 +}