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
322 additions
and
152 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; |
| 302 | + } else if (!nbr.equals(newNbr)) { | ||
| 303 | + if (newNbr.holdtime() == 0) { | ||
| 304 | + // Neighbor has shut down. Remove them and clean up | ||
| 305 | + pimNeighbors.remove(srcip, nbr); | ||
| 306 | + return; | ||
| 275 | } else { | 307 | } else { |
| 276 | - Integer previousGenid = nbr.getGenid(); | 308 | + // Neighbor has changed one of their options. |
| 277 | - nbr.addOptions(hello.getOptions()); | 309 | + pimNeighbors.put(srcip, newNbr); |
| 278 | - if (previousGenid != nbr.getGenid()) { | 310 | + nbr = newNbr; |
| 279 | - genidChanged = true; | ||
| 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; |
| 52 | 53 | ||
| 53 | - // Our timestamp for this neighbor | 54 | + private final long upTime; |
| 54 | - private Date lastRefresh; | ||
| 55 | 55 | ||
| 56 | - /** | 56 | + // Our timestamp for this neighbor |
| 57 | - * Construct a new PIM Neighbor. | 57 | + private long lastRefresh; |
| 58 | - * | ||
| 59 | - * @param ipAddr the IP Address of our new neighbor | ||
| 60 | - * @param opts option map | ||
| 61 | - */ | ||
| 62 | - public PIMNeighbor(IpAddress ipAddr, Map<Short, PIMHelloOption> opts) { | ||
| 63 | - this.ipAddr = ipAddr; | ||
| 64 | - this.addOptions(opts); | ||
| 65 | - } | ||
| 66 | 58 | ||
| 67 | /** | 59 | /** |
| 68 | - * Construct a new PIM neighbor. | 60 | + * Class constructor. |
| 69 | - * | 61 | + * |
| 70 | - * @param ipAddr the neighbors IP addr | 62 | + * @param ipAddress neighbor IP address |
| 71 | - * @param macAddr MAC address | 63 | + * @param macAddress neighbor MAC address |
| 72 | - */ | 64 | + * @param holdTime hold time |
| 73 | - public PIMNeighbor(IpAddress ipAddr, MacAddress macAddr) { | 65 | + * @param pruneDelay prune delay |
| 74 | - this.ipAddr = ipAddr; | 66 | + * @param priority priority |
| 75 | - this.macAddr = macAddr; | 67 | + * @param genId generation ID |
| 76 | - } | 68 | + */ |
| 69 | + public PIMNeighbor(IpAddress ipAddress, MacAddress macAddress, | ||
| 70 | + short holdTime, int pruneDelay, int priority, int genId) { | ||
| 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 | 77 | ||
| 78 | - /** | 78 | + this.upTime = System.currentTimeMillis(); |
| 79 | - * Get the MAC address of this neighbor. | ||
| 80 | - * | ||
| 81 | - * @return the mac address | ||
| 82 | - */ | ||
| 83 | - public MacAddress getMacaddr() { | ||
| 84 | - return macAddr; | ||
| 85 | } | 79 | } |
| 86 | 80 | ||
| 87 | /** | 81 | /** |
| 88 | - * Get the IP Address of our neighbor. | 82 | + * Gets the IP address of our neighbor. |
| 89 | * | 83 | * |
| 90 | * @return the IP address of our neighbor | 84 | * @return the IP address of our neighbor |
| 91 | */ | 85 | */ |
| 92 | - public IpAddress getIpaddr() { | 86 | + public IpAddress ipAddress() { |
| 93 | return ipAddr; | 87 | return ipAddr; |
| 94 | } | 88 | } |
| 95 | 89 | ||
| 96 | /** | 90 | /** |
| 97 | - * Set the IP address of our neighbor. | 91 | + * Gets the MAC address of this neighbor. |
| 98 | * | 92 | * |
| 99 | - * @param ipAddr our neighbors IP address | 93 | + * @return the mac address |
| 100 | */ | 94 | */ |
| 101 | - public void setIpaddr(IpAddress ipAddr) { | 95 | + public MacAddress macAddress() { |
| 102 | - this.ipAddr = ipAddr; | 96 | + return macAddr; |
| 103 | } | 97 | } |
| 104 | 98 | ||
| 105 | /** | 99 | /** |
| 106 | - * Get our neighbors holdTime. | 100 | + * Gets our neighbor's hold time. |
| 107 | * | 101 | * |
| 108 | - * @return the holdTime | 102 | + * @return the hold time |
| 109 | */ | 103 | */ |
| 110 | - public short getHoldtime() { | 104 | + public short holdtime() { |
| 111 | return holdTime; | 105 | return holdTime; |
| 112 | } | 106 | } |
| 113 | 107 | ||
| 114 | /** | 108 | /** |
| 115 | - * Set our neighbors holdTime. | 109 | + * Gets our neighbor's prune delay. |
| 116 | * | 110 | * |
| 117 | - * @param holdTime the holdTime | 111 | + * @return our neighbor's prune delay |
| 118 | */ | 112 | */ |
| 119 | - public void setHoldtime(short holdTime) { | 113 | + public int pruneDelay() { |
| 120 | - this.holdTime = holdTime; | 114 | + return pruneDelay; |
| 121 | } | 115 | } |
| 122 | 116 | ||
| 123 | /** | 117 | /** |
| 124 | - * Get our neighbors prune delay. | 118 | + * Gets our neighbor's priority. |
| 125 | * | 119 | * |
| 126 | - * @return our neighbors prune delay | 120 | + * @return our neighbor's priority |
| 127 | */ | 121 | */ |
| 128 | - public int getPruneDelay() { | 122 | + public int priority() { |
| 129 | - return pruneDelay; | 123 | + return priority; |
| 130 | } | 124 | } |
| 131 | 125 | ||
| 132 | /** | 126 | /** |
| 133 | - * Set our neighbors prune delay. | 127 | + * Gets our neighbor's generation ID. |
| 134 | * | 128 | * |
| 135 | - * @param pruneDelay the prune delay | 129 | + * @return our neighbor's generation ID |
| 136 | */ | 130 | */ |
| 137 | - public void setPruneDelay(int pruneDelay) { | 131 | + public int generationId() { |
| 138 | - this.pruneDelay = pruneDelay; | 132 | + return genId; |
| 139 | } | 133 | } |
| 140 | 134 | ||
| 141 | /** | 135 | /** |
| 142 | - * Get our neighbors priority. | 136 | + * Gets the last time we heard a HELLO from this neighbor. |
| 143 | * | 137 | * |
| 144 | - * @return our neighbors priority | 138 | + * @return last refresh time |
| 145 | */ | 139 | */ |
| 146 | - public int getPriority() { | 140 | + public long lastRefresh() { |
| 147 | - return priority; | 141 | + return lastRefresh; |
| 148 | } | 142 | } |
| 149 | 143 | ||
| 150 | /** | 144 | /** |
| 151 | - * Set our neighbors priority. | 145 | + * Gets the time that we first learnt of this neighbor. |
| 152 | * | 146 | * |
| 153 | - * @param priority our neighbors priority | 147 | + * @return up time |
| 154 | */ | 148 | */ |
| 155 | - public void setPriority(int priority) { | 149 | + public long upTime() { |
| 156 | - this.priority = priority; | 150 | + return upTime; |
| 157 | } | 151 | } |
| 158 | 152 | ||
| 159 | /** | 153 | /** |
| 160 | - * Get our neighbors Genid. | 154 | + * Refreshes this neighbor's last seen timestamp. |
| 161 | - * | ||
| 162 | - * @return our neighbor Genid | ||
| 163 | */ | 155 | */ |
| 164 | - public int getGenid() { | 156 | + public void refreshTimestamp() { |
| 165 | - return genId; | 157 | + lastRefresh = System.currentTimeMillis(); |
| 166 | } | 158 | } |
| 167 | 159 | ||
| 168 | /** | 160 | /** |
| 169 | - * Set our neighbors GenId. | 161 | + * Returns whether this neighbor is expired or not. |
| 170 | * | 162 | * |
| 171 | - * @param genId our neighbors GenId | 163 | + * @return true if the neighbor is expired, otherwise false |
| 172 | */ | 164 | */ |
| 173 | - public void setGenid(int genId) { | 165 | + public boolean isExpired() { |
| 174 | - this.genId = genId; | 166 | + return lastRefresh + TimeUnit.SECONDS.toMillis(holdTime) |
| 167 | + < System.currentTimeMillis(); | ||
| 175 | } | 168 | } |
| 176 | 169 | ||
| 177 | /** | 170 | /** |
| 178 | - * Add the options for this neighbor if needed. | 171 | + * Creates a PIM neighbor based on an IP, MAC, and collection of PIM HELLO |
| 179 | - * | 172 | + * options. |
| 180 | - * @param opts the options to be added/modified | 173 | + * |
| 181 | - * @return true if options changed, false if no option has changed | 174 | + * @param ipAddress neighbor IP address |
| 182 | - */ | 175 | + * @param macAddress neighbor MAC address |
| 183 | - public boolean addOptions(Map<Short, PIMHelloOption> opts) { | 176 | + * @param opts options from the PIM HELLO packet |
| 184 | - | 177 | + * @return new PIM neighbor |
| 185 | - boolean changed = false; | 178 | + */ |
| 186 | - | 179 | + public static PIMNeighbor createPimNeighbor(IpAddress ipAddress, |
| 187 | - for (PIMHelloOption opt : opts.values()) { | 180 | + MacAddress macAddress, |
| 188 | - Short otype = opt.getOptType(); | 181 | + Collection<PIMHelloOption> opts) { |
| 189 | - ByteBuffer val = ByteBuffer.wrap(opt.getValue()); | 182 | + |
| 190 | - | 183 | + int generationID = PIMHelloOption.DEFAULT_GENID; |
| 191 | - if (otype == PIMHelloOption.OPT_ADDRLIST) { | 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) { | ||
| 192 | // TODO: Will implement someday | 201 | // TODO: Will implement someday |
| 193 | - } else if (otype == PIMHelloOption.OPT_GENID) { | ||
| 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 | } | 202 | } |
| 205 | - } else if (otype == PIMHelloOption.OPT_PRIORITY) { | ||
| 206 | - int newval = val.getInt(); | ||
| 207 | - if (newval != priority) { | ||
| 208 | - priority = newval; | ||
| 209 | - changed = true; | ||
| 210 | } | 203 | } |
| 211 | - } else if (otype == PIMHelloOption.OPT_PRUNEDELAY) { | 204 | + |
| 212 | - int newval = val.getInt(); | 205 | + return new PIMNeighbor(ipAddress, macAddress, holdTime, pruneDelay, priority, generationID); |
| 213 | - if (newval != pruneDelay) { | ||
| 214 | - pruneDelay = newval; | ||
| 215 | - changed = true; | ||
| 216 | } | 206 | } |
| 217 | - } else { | 207 | + |
| 218 | - log.warn("received unknown pim hello options" + otype); | 208 | + @Override |
| 209 | + public boolean equals(Object other) { | ||
| 210 | + if (!(other instanceof PIMNeighbor)) { | ||
| 211 | + return false; | ||
| 219 | } | 212 | } |
| 213 | + | ||
| 214 | + PIMNeighbor that = (PIMNeighbor) other; | ||
| 215 | + | ||
| 216 | + return this.ipAddr.equals(that.ipAddress()) && | ||
| 217 | + this.macAddr.equals(that.macAddress()) && | ||
| 218 | + this.genId == that.genId && | ||
| 219 | + this.holdTime == that.holdTime && | ||
| 220 | + this.priority == that.priority; | ||
| 220 | } | 221 | } |
| 221 | - return changed; | 222 | + |
| 223 | + @Override | ||
| 224 | + public int hashCode() { | ||
| 225 | + return Objects.hash(ipAddr, macAddr, genId, holdTime, priority); | ||
| 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