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
Showing
6 changed files
with
321 additions
and
151 deletions
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 | +} |
-
Please register or login to post a comment