Showing
20 changed files
with
853 additions
and
129 deletions
| 1 | +package org.onlab.onos.cli.net; | ||
| 2 | + | ||
| 3 | +import static com.google.common.collect.Lists.newArrayList; | ||
| 4 | + | ||
| 5 | +import java.util.Collections; | ||
| 6 | +import java.util.Comparator; | ||
| 7 | +import java.util.List; | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +import org.apache.karaf.shell.commands.Command; | ||
| 11 | +import org.onlab.onos.cli.AbstractShellCommand; | ||
| 12 | +import org.onlab.onos.net.Device; | ||
| 13 | +import org.onlab.onos.net.device.DeviceService; | ||
| 14 | +import org.onlab.onos.net.flow.FlowRule; | ||
| 15 | +import org.onlab.onos.net.flow.FlowRuleService; | ||
| 16 | + | ||
| 17 | +import com.google.common.collect.Maps; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * Lists all currently-known hosts. | ||
| 21 | + */ | ||
| 22 | +@Command(scope = "onos", name = "flows", | ||
| 23 | +description = "Lists all currently-known flows.") | ||
| 24 | +public class FlowsListCommand extends AbstractShellCommand { | ||
| 25 | + | ||
| 26 | + private static final String FMT = | ||
| 27 | + " id=%s, selector=%s, treatment=%s, state=%s"; | ||
| 28 | + | ||
| 29 | + protected static final Comparator<FlowRule> ID_COMPARATOR = new Comparator<FlowRule>() { | ||
| 30 | + @Override | ||
| 31 | + public int compare(FlowRule f1, FlowRule f2) { | ||
| 32 | + return Long.valueOf(f1.id().value()).compareTo(f2.id().value()); | ||
| 33 | + } | ||
| 34 | + }; | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + protected Object doExecute() throws Exception { | ||
| 38 | + DeviceService deviceService = getService(DeviceService.class); | ||
| 39 | + FlowRuleService service = getService(FlowRuleService.class); | ||
| 40 | + Map<Device, List<FlowRule>> flows = getSortedFlows(deviceService, service); | ||
| 41 | + for (Device d : deviceService.getDevices()) { | ||
| 42 | + printFlows(d, flows.get(d)); | ||
| 43 | + } | ||
| 44 | + return null; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * Returns the list of devices sorted using the device ID URIs. | ||
| 50 | + * | ||
| 51 | + * @param service device service | ||
| 52 | + * @return sorted device list | ||
| 53 | + */ | ||
| 54 | + protected Map<Device, List<FlowRule>> getSortedFlows(DeviceService deviceService, FlowRuleService service) { | ||
| 55 | + Map<Device, List<FlowRule>> flows = Maps.newHashMap(); | ||
| 56 | + List<FlowRule> rules; | ||
| 57 | + for (Device d : deviceService.getDevices()) { | ||
| 58 | + rules = newArrayList(service.getFlowEntries(d.id())); | ||
| 59 | + Collections.sort(rules, ID_COMPARATOR); | ||
| 60 | + flows.put(d, rules); | ||
| 61 | + } | ||
| 62 | + return flows; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * Prints flows. | ||
| 67 | + * @param d the device | ||
| 68 | + * @param flows the set of flows for that device. | ||
| 69 | + */ | ||
| 70 | + protected void printFlows(Device d, List<FlowRule> flows) { | ||
| 71 | + print("Device: " + d.id()); | ||
| 72 | + for (FlowRule f : flows) { | ||
| 73 | + print(FMT, f.id().value(), f.selector(), f.treatment(), f.state()); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -2,6 +2,9 @@ | ... | @@ -2,6 +2,9 @@ |
| 2 | 2 | ||
| 3 | <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> | 3 | <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> |
| 4 | <command> | 4 | <command> |
| 5 | + <action class="org.onlab.onos.cli.net.FlowsListCommand"/> | ||
| 6 | + </command> | ||
| 7 | + <command> | ||
| 5 | <action class="org.onlab.onos.cli.net.DevicesListCommand"/> | 8 | <action class="org.onlab.onos.cli.net.DevicesListCommand"/> |
| 6 | </command> | 9 | </command> |
| 7 | <command> | 10 | <command> | ... | ... |
| 1 | +package org.onlab.onos.cluster; | ||
| 2 | + | ||
| 3 | +import org.onlab.onos.event.AbstractEvent; | ||
| 4 | +import org.onlab.onos.net.DeviceId; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * Describes infrastructure device event. | ||
| 8 | + */ | ||
| 9 | +public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceId> { | ||
| 10 | + | ||
| 11 | + InstanceId master; | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * Type of mastership events. | ||
| 15 | + */ | ||
| 16 | + public enum Type { | ||
| 17 | + /** | ||
| 18 | + * Signifies that the master for a device has changed. | ||
| 19 | + */ | ||
| 20 | + MASTER_CHANGED | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * Creates an event of a given type and for the specified device, master, | ||
| 25 | + * and the current time. | ||
| 26 | + * | ||
| 27 | + * @param type device event type | ||
| 28 | + * @param device event device subject | ||
| 29 | + * @param master master ID subject | ||
| 30 | + */ | ||
| 31 | + protected MastershipEvent(Type type, DeviceId device, InstanceId master) { | ||
| 32 | + super(type, device); | ||
| 33 | + this.master = master; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * Creates an event of a given type and for the specified device, master, | ||
| 38 | + * and time. | ||
| 39 | + * | ||
| 40 | + * @param type mastership event type | ||
| 41 | + * @param device event device subject | ||
| 42 | + * @param master master ID subject | ||
| 43 | + * @param time occurrence time | ||
| 44 | + */ | ||
| 45 | + protected MastershipEvent(Type type, DeviceId device, InstanceId master, long time) { | ||
| 46 | + super(type, device, time); | ||
| 47 | + this.master = master; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * Returns the current master's ID as a subject. | ||
| 52 | + * | ||
| 53 | + * @return master ID subject | ||
| 54 | + */ | ||
| 55 | + public InstanceId master() { | ||
| 56 | + return master; | ||
| 57 | + } | ||
| 58 | +} |
| 1 | package org.onlab.onos.cluster; | 1 | package org.onlab.onos.cluster; |
| 2 | 2 | ||
| 3 | +import java.util.Set; | ||
| 4 | + | ||
| 5 | +import org.onlab.onos.net.DeviceId; | ||
| 6 | +import org.onlab.onos.net.MastershipRole; | ||
| 7 | + | ||
| 3 | /** | 8 | /** |
| 4 | * Service responsible for determining the controller instance mastership of | 9 | * Service responsible for determining the controller instance mastership of |
| 5 | * a device in a clustered environment. This is the central authority for | 10 | * a device in a clustered environment. This is the central authority for |
| ... | @@ -8,12 +13,42 @@ package org.onlab.onos.cluster; | ... | @@ -8,12 +13,42 @@ package org.onlab.onos.cluster; |
| 8 | */ | 13 | */ |
| 9 | public interface MastershipService { | 14 | public interface MastershipService { |
| 10 | 15 | ||
| 11 | - // InstanceId getMasterFor(DeviceId deviceId) | 16 | + /** |
| 12 | - // Set<DeviceId> getDevicesOf(InstanceId instanceId); | 17 | + * Returns the current master for a given device. |
| 18 | + * | ||
| 19 | + * @param deviceId the identifier of the device | ||
| 20 | + * @return the ID of the master controller for the device | ||
| 21 | + */ | ||
| 22 | + InstanceId getMasterFor(DeviceId deviceId); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Returns the devices for which a controller is master. | ||
| 26 | + * | ||
| 27 | + * @param instanceId the ID of the controller | ||
| 28 | + * @return a set of device IDs | ||
| 29 | + */ | ||
| 30 | + Set<DeviceId> getDevicesOf(InstanceId instanceId); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * Returns the mastership status of this controller for a given device. | ||
| 34 | + * | ||
| 35 | + * @param deviceId the the identifier of the device | ||
| 36 | + * @return the role of this controller instance | ||
| 37 | + */ | ||
| 38 | + MastershipRole requestRoleFor(DeviceId deviceId); | ||
| 13 | 39 | ||
| 14 | - // MastershipRole requestRoleFor(DeviceId deviceId); | 40 | + /** |
| 41 | + * Adds the specified mastership listener. | ||
| 42 | + * | ||
| 43 | + * @param listener the mastership listener | ||
| 44 | + */ | ||
| 45 | + void addListener(MastershipListener listener); | ||
| 15 | 46 | ||
| 16 | - // addListener/removeLister(MastershipListener listener); | 47 | + /** |
| 17 | - // types of events would be MASTER_CHANGED (subject ==> deviceId; master ==> instanceId) | 48 | + * Removes the specified device listener. |
| 49 | + * | ||
| 50 | + * @param listener the mastership listener | ||
| 51 | + */ | ||
| 52 | + void removeListemer(MastershipListener listener); | ||
| 18 | 53 | ||
| 19 | } | 54 | } | ... | ... |
| 1 | package org.onlab.onos.net.flow; | 1 | package org.onlab.onos.net.flow; |
| 2 | 2 | ||
| 3 | import static com.google.common.base.MoreObjects.toStringHelper; | 3 | import static com.google.common.base.MoreObjects.toStringHelper; |
| 4 | +import static org.slf4j.LoggerFactory.getLogger; | ||
| 4 | 5 | ||
| 5 | import java.util.Objects; | 6 | import java.util.Objects; |
| 6 | 7 | ||
| 7 | import org.onlab.onos.net.DeviceId; | 8 | import org.onlab.onos.net.DeviceId; |
| 9 | +import org.slf4j.Logger; | ||
| 8 | 10 | ||
| 9 | public class DefaultFlowRule implements FlowRule { | 11 | public class DefaultFlowRule implements FlowRule { |
| 10 | 12 | ||
| 13 | + private final Logger log = getLogger(getClass()); | ||
| 14 | + | ||
| 11 | private final DeviceId deviceId; | 15 | private final DeviceId deviceId; |
| 12 | private final int priority; | 16 | private final int priority; |
| 13 | private final TrafficSelector selector; | 17 | private final TrafficSelector selector; |
| 14 | private final TrafficTreatment treatment; | 18 | private final TrafficTreatment treatment; |
| 15 | - private final FlowId id; | ||
| 16 | private final long created; | 19 | private final long created; |
| 17 | private final long life; | 20 | private final long life; |
| 18 | - private final long idle; | ||
| 19 | private final long packets; | 21 | private final long packets; |
| 20 | private final long bytes; | 22 | private final long bytes; |
| 23 | + private final FlowRuleState state; | ||
| 24 | + | ||
| 25 | + private final FlowId id; | ||
| 26 | + | ||
| 27 | + public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, | ||
| 28 | + TrafficTreatment treatment, int priority, FlowRuleState state, | ||
| 29 | + long life, long packets, long bytes, long flowId) { | ||
| 30 | + this.deviceId = deviceId; | ||
| 31 | + this.priority = priority; | ||
| 32 | + this.selector = selector; | ||
| 33 | + this.treatment = treatment; | ||
| 34 | + this.state = state; | ||
| 35 | + | ||
| 36 | + this.id = FlowId.valueOf(flowId); | ||
| 21 | 37 | ||
| 38 | + this.life = life; | ||
| 39 | + this.packets = packets; | ||
| 40 | + this.bytes = bytes; | ||
| 41 | + this.created = System.currentTimeMillis(); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, | ||
| 45 | + TrafficTreatment treatement, int priority) { | ||
| 46 | + this(deviceId, selector, treatement, priority, FlowRuleState.CREATED); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public DefaultFlowRule(FlowRule rule, FlowRuleState state) { | ||
| 50 | + this(rule.deviceId(), rule.selector(), rule.treatment(), | ||
| 51 | + rule.priority(), state, rule.id()); | ||
| 52 | + } | ||
| 22 | 53 | ||
| 23 | - public DefaultFlowRule(DeviceId deviceId, | 54 | + private DefaultFlowRule(DeviceId deviceId, |
| 24 | - TrafficSelector selector, TrafficTreatment treatment, int priority) { | 55 | + TrafficSelector selector, TrafficTreatment treatment, |
| 56 | + int priority, FlowRuleState state) { | ||
| 25 | this.deviceId = deviceId; | 57 | this.deviceId = deviceId; |
| 26 | this.priority = priority; | 58 | this.priority = priority; |
| 27 | this.selector = selector; | 59 | this.selector = selector; |
| 28 | this.treatment = treatment; | 60 | this.treatment = treatment; |
| 61 | + this.state = state; | ||
| 29 | this.life = 0; | 62 | this.life = 0; |
| 30 | - this.idle = 0; | ||
| 31 | this.packets = 0; | 63 | this.packets = 0; |
| 32 | this.bytes = 0; | 64 | this.bytes = 0; |
| 33 | this.id = FlowId.valueOf(this.hashCode()); | 65 | this.id = FlowId.valueOf(this.hashCode()); |
| 34 | this.created = System.currentTimeMillis(); | 66 | this.created = System.currentTimeMillis(); |
| 35 | } | 67 | } |
| 36 | 68 | ||
| 37 | - public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, | 69 | + private DefaultFlowRule(DeviceId deviceId, |
| 38 | - TrafficTreatment treatment, int priority, | 70 | + TrafficSelector selector, TrafficTreatment treatment, |
| 39 | - long life, long idle, long packets, long bytes, Integer flowId) { | 71 | + int priority, FlowRuleState state, FlowId flowId) { |
| 40 | this.deviceId = deviceId; | 72 | this.deviceId = deviceId; |
| 41 | this.priority = priority; | 73 | this.priority = priority; |
| 42 | this.selector = selector; | 74 | this.selector = selector; |
| 43 | this.treatment = treatment; | 75 | this.treatment = treatment; |
| 44 | - | 76 | + this.state = state; |
| 45 | - this.id = FlowId.valueOf(flowId); | 77 | + this.life = 0; |
| 46 | - | 78 | + this.packets = 0; |
| 47 | - this.life = life; | 79 | + this.bytes = 0; |
| 48 | - this.idle = idle; | 80 | + this.id = flowId; |
| 49 | - this.packets = packets; | ||
| 50 | - this.bytes = bytes; | ||
| 51 | this.created = System.currentTimeMillis(); | 81 | this.created = System.currentTimeMillis(); |
| 52 | } | 82 | } |
| 53 | 83 | ||
| ... | @@ -83,11 +113,6 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -83,11 +113,6 @@ public class DefaultFlowRule implements FlowRule { |
| 83 | } | 113 | } |
| 84 | 114 | ||
| 85 | @Override | 115 | @Override |
| 86 | - public long idleMillis() { | ||
| 87 | - return idle; | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Override | ||
| 91 | public long packets() { | 116 | public long packets() { |
| 92 | return packets; | 117 | return packets; |
| 93 | } | 118 | } |
| ... | @@ -98,6 +123,12 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -98,6 +123,12 @@ public class DefaultFlowRule implements FlowRule { |
| 98 | } | 123 | } |
| 99 | 124 | ||
| 100 | @Override | 125 | @Override |
| 126 | + public FlowRuleState state() { | ||
| 127 | + return this.state; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + | ||
| 131 | + @Override | ||
| 101 | /* | 132 | /* |
| 102 | * The priority and statistics can change on a given treatment and selector | 133 | * The priority and statistics can change on a given treatment and selector |
| 103 | * | 134 | * |
| ... | @@ -116,18 +147,14 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -116,18 +147,14 @@ public class DefaultFlowRule implements FlowRule { |
| 116 | * @see java.lang.Object#equals(java.lang.Object) | 147 | * @see java.lang.Object#equals(java.lang.Object) |
| 117 | */ | 148 | */ |
| 118 | public boolean equals(Object obj) { | 149 | public boolean equals(Object obj) { |
| 150 | + | ||
| 151 | + if (this == obj) { | ||
| 152 | + return true; | ||
| 153 | + } | ||
| 119 | if (obj instanceof FlowRule) { | 154 | if (obj instanceof FlowRule) { |
| 120 | DefaultFlowRule that = (DefaultFlowRule) obj; | 155 | DefaultFlowRule that = (DefaultFlowRule) obj; |
| 121 | - if (!this.deviceId().equals(that.deviceId())) { | 156 | + return Objects.equals(deviceId, that.deviceId) && |
| 122 | - return false; | 157 | + Objects.equals(id, that.id); |
| 123 | - } | ||
| 124 | - if (!this.treatment().equals(that.treatment())) { | ||
| 125 | - return false; | ||
| 126 | - } | ||
| 127 | - if (!this.selector().equals(that.selector())) { | ||
| 128 | - return false; | ||
| 129 | - } | ||
| 130 | - return true; | ||
| 131 | } | 158 | } |
| 132 | return false; | 159 | return false; |
| 133 | } | 160 | } |
| ... | @@ -141,8 +168,8 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -141,8 +168,8 @@ public class DefaultFlowRule implements FlowRule { |
| 141 | .add("selector", selector) | 168 | .add("selector", selector) |
| 142 | .add("treatment", treatment) | 169 | .add("treatment", treatment) |
| 143 | .add("created", created) | 170 | .add("created", created) |
| 171 | + .add("state", state) | ||
| 144 | .toString(); | 172 | .toString(); |
| 145 | } | 173 | } |
| 146 | 174 | ||
| 147 | - | ||
| 148 | } | 175 | } | ... | ... |
| 1 | package org.onlab.onos.net.flow; | 1 | package org.onlab.onos.net.flow; |
| 2 | 2 | ||
| 3 | +import com.google.common.base.Objects; | ||
| 4 | + | ||
| 3 | /** | 5 | /** |
| 4 | * Representation of a Flow ID. | 6 | * Representation of a Flow ID. |
| 5 | */ | 7 | */ |
| 6 | public final class FlowId { | 8 | public final class FlowId { |
| 7 | 9 | ||
| 8 | - private final int flowid; | 10 | + private final long flowid; |
| 9 | 11 | ||
| 10 | - private FlowId(int id) { | 12 | + private FlowId(long id) { |
| 11 | this.flowid = id; | 13 | this.flowid = id; |
| 12 | } | 14 | } |
| 13 | 15 | ||
| 14 | - public static FlowId valueOf(int id) { | 16 | + public static FlowId valueOf(long id) { |
| 15 | return new FlowId(id); | 17 | return new FlowId(id); |
| 16 | } | 18 | } |
| 17 | 19 | ||
| 18 | - public int value() { | 20 | + public long value() { |
| 19 | return flowid; | 21 | return flowid; |
| 20 | } | 22 | } |
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public boolean equals(Object obj) { | ||
| 26 | + if (this == obj) { | ||
| 27 | + return true; | ||
| 28 | + } | ||
| 29 | + if (obj.getClass() == this.getClass()) { | ||
| 30 | + FlowId that = (FlowId) obj; | ||
| 31 | + return Objects.equal(this.flowid, that.flowid); | ||
| 32 | + } | ||
| 33 | + return false; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + public int hashCode() { | ||
| 38 | + return Objects.hashCode(this.flowid); | ||
| 39 | + } | ||
| 21 | } | 40 | } | ... | ... |
| ... | @@ -8,6 +8,42 @@ import org.onlab.onos.net.DeviceId; | ... | @@ -8,6 +8,42 @@ import org.onlab.onos.net.DeviceId; |
| 8 | */ | 8 | */ |
| 9 | public interface FlowRule { | 9 | public interface FlowRule { |
| 10 | 10 | ||
| 11 | + | ||
| 12 | + public enum FlowRuleState { | ||
| 13 | + /** | ||
| 14 | + * Indicates that this rule has been created. | ||
| 15 | + */ | ||
| 16 | + CREATED, | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * Indicates that this rule has been submitted for addition. | ||
| 20 | + * Not necessarily in the flow table. | ||
| 21 | + */ | ||
| 22 | + PENDING_ADD, | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Rule has been added which means it is in the flow table. | ||
| 26 | + */ | ||
| 27 | + ADDED, | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * Flow has been marked for removal, might still be in flow table. | ||
| 31 | + */ | ||
| 32 | + PENDING_REMOVE, | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * Flow has been removed from flow table and can be purged. | ||
| 36 | + */ | ||
| 37 | + REMOVED | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * Returns the flow rule state. | ||
| 42 | + * | ||
| 43 | + * @return flow rule state | ||
| 44 | + */ | ||
| 45 | + FlowRuleState state(); | ||
| 46 | + | ||
| 11 | //TODO: build cookie value | 47 | //TODO: build cookie value |
| 12 | /** | 48 | /** |
| 13 | * Returns the ID of this flow. | 49 | * Returns the ID of this flow. |
| ... | @@ -54,13 +90,6 @@ public interface FlowRule { | ... | @@ -54,13 +90,6 @@ public interface FlowRule { |
| 54 | long lifeMillis(); | 90 | long lifeMillis(); |
| 55 | 91 | ||
| 56 | /** | 92 | /** |
| 57 | - * Returns the number of milliseconds this flow rule has been idle. | ||
| 58 | - * | ||
| 59 | - * @return number of millis | ||
| 60 | - */ | ||
| 61 | - long idleMillis(); | ||
| 62 | - | ||
| 63 | - /** | ||
| 64 | * Returns the number of packets this flow rule has matched. | 93 | * Returns the number of packets this flow rule has matched. |
| 65 | * | 94 | * |
| 66 | * @return number of packets | 95 | * @return number of packets | ... | ... |
| ... | @@ -19,7 +19,12 @@ public class FlowRuleEvent extends AbstractEvent<FlowRuleEvent.Type, FlowRule> { | ... | @@ -19,7 +19,12 @@ public class FlowRuleEvent extends AbstractEvent<FlowRuleEvent.Type, FlowRule> { |
| 19 | /** | 19 | /** |
| 20 | * Signifies that a flow rule has been removed. | 20 | * Signifies that a flow rule has been removed. |
| 21 | */ | 21 | */ |
| 22 | - RULE_REMOVED | 22 | + RULE_REMOVED, |
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Signifies that a rule has been updated. | ||
| 26 | + */ | ||
| 27 | + RULE_UPDATED | ||
| 23 | } | 28 | } |
| 24 | 29 | ||
| 25 | /** | 30 | /** | ... | ... |
| 1 | package org.onlab.onos.net.flow; | 1 | package org.onlab.onos.net.flow; |
| 2 | 2 | ||
| 3 | +import org.onlab.onos.net.DeviceId; | ||
| 3 | import org.onlab.onos.net.provider.ProviderService; | 4 | import org.onlab.onos.net.provider.ProviderService; |
| 4 | 5 | ||
| 5 | /** | 6 | /** |
| ... | @@ -23,6 +24,13 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide | ... | @@ -23,6 +24,13 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide |
| 23 | void flowMissing(FlowRule flowRule); | 24 | void flowMissing(FlowRule flowRule); |
| 24 | 25 | ||
| 25 | /** | 26 | /** |
| 27 | + * Signals that a flow rule is on the switch but not in the store. | ||
| 28 | + * | ||
| 29 | + * @param flowRule the extra flow rule | ||
| 30 | + */ | ||
| 31 | + void extraneousFlow(FlowRule flowRule); | ||
| 32 | + | ||
| 33 | + /** | ||
| 26 | * Signals that a flow rule was indeed added. | 34 | * Signals that a flow rule was indeed added. |
| 27 | * | 35 | * |
| 28 | * @param flowRule the added flow rule | 36 | * @param flowRule the added flow rule |
| ... | @@ -35,6 +43,8 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide | ... | @@ -35,6 +43,8 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide |
| 35 | * | 43 | * |
| 36 | * @param flowRules collection of flow rules | 44 | * @param flowRules collection of flow rules |
| 37 | */ | 45 | */ |
| 38 | - void pushFlowMetrics(Iterable<FlowRule> flowRules); | 46 | + void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules); |
| 47 | + | ||
| 48 | + | ||
| 39 | 49 | ||
| 40 | } | 50 | } | ... | ... |
| 1 | package org.onlab.onos.net.flow; | 1 | package org.onlab.onos.net.flow; |
| 2 | 2 | ||
| 3 | -import java.util.List; | ||
| 4 | - | ||
| 5 | import org.onlab.onos.net.DeviceId; | 3 | import org.onlab.onos.net.DeviceId; |
| 6 | 4 | ||
| 7 | /** | 5 | /** |
| ... | @@ -31,10 +29,8 @@ public interface FlowRuleService { | ... | @@ -31,10 +29,8 @@ public interface FlowRuleService { |
| 31 | * device reconnects to the controller. | 29 | * device reconnects to the controller. |
| 32 | * | 30 | * |
| 33 | * @param flowRules one or more flow rules | 31 | * @param flowRules one or more flow rules |
| 34 | - * throws SomeKindOfException that indicates which ones were applied and | ||
| 35 | - * which ones failed | ||
| 36 | */ | 32 | */ |
| 37 | - List<FlowRule> applyFlowRules(FlowRule... flowRules); | 33 | + void applyFlowRules(FlowRule... flowRules); |
| 38 | 34 | ||
| 39 | /** | 35 | /** |
| 40 | * Removes the specified flow rules from their respective devices. If the | 36 | * Removes the specified flow rules from their respective devices. If the | ... | ... |
| ... | @@ -16,12 +16,18 @@ public interface FlowRuleStore { | ... | @@ -16,12 +16,18 @@ public interface FlowRuleStore { |
| 16 | Iterable<FlowRule> getFlowEntries(DeviceId deviceId); | 16 | Iterable<FlowRule> getFlowEntries(DeviceId deviceId); |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * Stores a new flow rule, and generates a FlowRule for it. | 19 | + * Stores a new flow rule without generating events. |
| 20 | * | 20 | * |
| 21 | * @param rule the flow rule to add | 21 | * @param rule the flow rule to add |
| 22 | - * @return a flow entry | ||
| 23 | */ | 22 | */ |
| 24 | - FlowRule storeFlowRule(FlowRule rule); | 23 | + void storeFlowRule(FlowRule rule); |
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * Deletes a flow rule without generating events. | ||
| 27 | + * | ||
| 28 | + * @param rule the flow rule to delete | ||
| 29 | + */ | ||
| 30 | + void deleteFlowRule(FlowRule rule); | ||
| 25 | 31 | ||
| 26 | /** | 32 | /** |
| 27 | * Stores a new flow rule, or updates an existing entry. | 33 | * Stores a new flow rule, or updates an existing entry. | ... | ... |
| ... | @@ -3,7 +3,7 @@ package org.onlab.onos.net.flow.impl; | ... | @@ -3,7 +3,7 @@ package org.onlab.onos.net.flow.impl; |
| 3 | import static com.google.common.base.Preconditions.checkNotNull; | 3 | import static com.google.common.base.Preconditions.checkNotNull; |
| 4 | import static org.slf4j.LoggerFactory.getLogger; | 4 | import static org.slf4j.LoggerFactory.getLogger; |
| 5 | 5 | ||
| 6 | -import java.util.ArrayList; | 6 | +import java.util.Iterator; |
| 7 | import java.util.List; | 7 | import java.util.List; |
| 8 | 8 | ||
| 9 | import org.apache.felix.scr.annotations.Activate; | 9 | import org.apache.felix.scr.annotations.Activate; |
| ... | @@ -17,7 +17,9 @@ import org.onlab.onos.event.EventDeliveryService; | ... | @@ -17,7 +17,9 @@ import org.onlab.onos.event.EventDeliveryService; |
| 17 | import org.onlab.onos.net.Device; | 17 | import org.onlab.onos.net.Device; |
| 18 | import org.onlab.onos.net.DeviceId; | 18 | import org.onlab.onos.net.DeviceId; |
| 19 | import org.onlab.onos.net.device.DeviceService; | 19 | import org.onlab.onos.net.device.DeviceService; |
| 20 | +import org.onlab.onos.net.flow.DefaultFlowRule; | ||
| 20 | import org.onlab.onos.net.flow.FlowRule; | 21 | import org.onlab.onos.net.flow.FlowRule; |
| 22 | +import org.onlab.onos.net.flow.FlowRule.FlowRuleState; | ||
| 21 | import org.onlab.onos.net.flow.FlowRuleEvent; | 23 | import org.onlab.onos.net.flow.FlowRuleEvent; |
| 22 | import org.onlab.onos.net.flow.FlowRuleListener; | 24 | import org.onlab.onos.net.flow.FlowRuleListener; |
| 23 | import org.onlab.onos.net.flow.FlowRuleProvider; | 25 | import org.onlab.onos.net.flow.FlowRuleProvider; |
| ... | @@ -29,17 +31,19 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry; | ... | @@ -29,17 +31,19 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry; |
| 29 | import org.onlab.onos.net.provider.AbstractProviderService; | 31 | import org.onlab.onos.net.provider.AbstractProviderService; |
| 30 | import org.slf4j.Logger; | 32 | import org.slf4j.Logger; |
| 31 | 33 | ||
| 34 | +import com.google.common.collect.Lists; | ||
| 35 | + | ||
| 32 | @Component(immediate = true) | 36 | @Component(immediate = true) |
| 33 | @Service | 37 | @Service |
| 34 | public class FlowRuleManager | 38 | public class FlowRuleManager |
| 35 | - extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService> | 39 | +extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService> |
| 36 | - implements FlowRuleService, FlowRuleProviderRegistry { | 40 | +implements FlowRuleService, FlowRuleProviderRegistry { |
| 37 | 41 | ||
| 38 | public static final String FLOW_RULE_NULL = "FlowRule cannot be null"; | 42 | public static final String FLOW_RULE_NULL = "FlowRule cannot be null"; |
| 39 | private final Logger log = getLogger(getClass()); | 43 | private final Logger log = getLogger(getClass()); |
| 40 | 44 | ||
| 41 | private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener> | 45 | private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener> |
| 42 | - listenerRegistry = new AbstractListenerRegistry<>(); | 46 | + listenerRegistry = new AbstractListenerRegistry<>(); |
| 43 | 47 | ||
| 44 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 48 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 45 | protected FlowRuleStore store; | 49 | protected FlowRuleStore store; |
| ... | @@ -68,27 +72,24 @@ public class FlowRuleManager | ... | @@ -68,27 +72,24 @@ public class FlowRuleManager |
| 68 | } | 72 | } |
| 69 | 73 | ||
| 70 | @Override | 74 | @Override |
| 71 | - public List<FlowRule> applyFlowRules(FlowRule... flowRules) { | 75 | + public void applyFlowRules(FlowRule... flowRules) { |
| 72 | - List<FlowRule> entries = new ArrayList<FlowRule>(); | ||
| 73 | - | ||
| 74 | for (int i = 0; i < flowRules.length; i++) { | 76 | for (int i = 0; i < flowRules.length; i++) { |
| 75 | - FlowRule f = flowRules[i]; | 77 | + FlowRule f = new DefaultFlowRule(flowRules[i], FlowRuleState.PENDING_ADD); |
| 76 | final Device device = deviceService.getDevice(f.deviceId()); | 78 | final Device device = deviceService.getDevice(f.deviceId()); |
| 77 | final FlowRuleProvider frp = getProvider(device.providerId()); | 79 | final FlowRuleProvider frp = getProvider(device.providerId()); |
| 78 | - entries.add(store.storeFlowRule(f)); | 80 | + store.storeFlowRule(f); |
| 79 | frp.applyFlowRule(f); | 81 | frp.applyFlowRule(f); |
| 80 | } | 82 | } |
| 81 | - | ||
| 82 | - return entries; | ||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | @Override | 85 | @Override |
| 86 | public void removeFlowRules(FlowRule... flowRules) { | 86 | public void removeFlowRules(FlowRule... flowRules) { |
| 87 | + FlowRule f; | ||
| 87 | for (int i = 0; i < flowRules.length; i++) { | 88 | for (int i = 0; i < flowRules.length; i++) { |
| 88 | - FlowRule f = flowRules[i]; | 89 | + f = new DefaultFlowRule(flowRules[i], FlowRuleState.PENDING_REMOVE); |
| 89 | final Device device = deviceService.getDevice(f.deviceId()); | 90 | final Device device = deviceService.getDevice(f.deviceId()); |
| 90 | final FlowRuleProvider frp = getProvider(device.providerId()); | 91 | final FlowRuleProvider frp = getProvider(device.providerId()); |
| 91 | - store.removeFlowRule(f); | 92 | + store.deleteFlowRule(f); |
| 92 | frp.removeFlowRule(f); | 93 | frp.removeFlowRule(f); |
| 93 | } | 94 | } |
| 94 | 95 | ||
| ... | @@ -111,8 +112,8 @@ public class FlowRuleManager | ... | @@ -111,8 +112,8 @@ public class FlowRuleManager |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | private class InternalFlowRuleProviderService | 114 | private class InternalFlowRuleProviderService |
| 114 | - extends AbstractProviderService<FlowRuleProvider> | 115 | + extends AbstractProviderService<FlowRuleProvider> |
| 115 | - implements FlowRuleProviderService { | 116 | + implements FlowRuleProviderService { |
| 116 | 117 | ||
| 117 | protected InternalFlowRuleProviderService(FlowRuleProvider provider) { | 118 | protected InternalFlowRuleProviderService(FlowRuleProvider provider) { |
| 118 | super(provider); | 119 | super(provider); |
| ... | @@ -134,22 +135,30 @@ public class FlowRuleManager | ... | @@ -134,22 +135,30 @@ public class FlowRuleManager |
| 134 | public void flowMissing(FlowRule flowRule) { | 135 | public void flowMissing(FlowRule flowRule) { |
| 135 | checkNotNull(flowRule, FLOW_RULE_NULL); | 136 | checkNotNull(flowRule, FLOW_RULE_NULL); |
| 136 | checkValidity(); | 137 | checkValidity(); |
| 137 | - // TODO Auto-generated method stub | 138 | + log.info("Flow {} has not been installed.", flowRule); |
| 138 | 139 | ||
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | @Override | 142 | @Override |
| 143 | + public void extraneousFlow(FlowRule flowRule) { | ||
| 144 | + checkNotNull(flowRule, FLOW_RULE_NULL); | ||
| 145 | + checkValidity(); | ||
| 146 | + log.info("Flow {} is on switch but not in store.", flowRule); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + @Override | ||
| 142 | public void flowAdded(FlowRule flowRule) { | 150 | public void flowAdded(FlowRule flowRule) { |
| 143 | checkNotNull(flowRule, FLOW_RULE_NULL); | 151 | checkNotNull(flowRule, FLOW_RULE_NULL); |
| 144 | checkValidity(); | 152 | checkValidity(); |
| 145 | 153 | ||
| 146 | FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule); | 154 | FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule); |
| 147 | if (event == null) { | 155 | if (event == null) { |
| 148 | - log.debug("Flow {} updated", flowRule); | 156 | + log.debug("No flow store event generated."); |
| 149 | } else { | 157 | } else { |
| 150 | - log.debug("Flow {} added", flowRule); | 158 | + log.debug("Flow {} {}", flowRule, event.type()); |
| 151 | post(event); | 159 | post(event); |
| 152 | } | 160 | } |
| 161 | + | ||
| 153 | } | 162 | } |
| 154 | 163 | ||
| 155 | // Posts the specified event to the local event dispatcher. | 164 | // Posts the specified event to the local event dispatcher. |
| ... | @@ -160,9 +169,25 @@ public class FlowRuleManager | ... | @@ -160,9 +169,25 @@ public class FlowRuleManager |
| 160 | } | 169 | } |
| 161 | 170 | ||
| 162 | @Override | 171 | @Override |
| 163 | - public void pushFlowMetrics(Iterable<FlowRule> flowEntries) { | 172 | + public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) { |
| 164 | - // TODO Auto-generated method stub | 173 | + List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId)); |
| 165 | - | 174 | + |
| 175 | + Iterator<FlowRule> switchRulesIterator = flowEntries.iterator(); | ||
| 176 | + | ||
| 177 | + while (switchRulesIterator.hasNext()) { | ||
| 178 | + FlowRule rule = switchRulesIterator.next(); | ||
| 179 | + if (storedRules.remove(rule)) { | ||
| 180 | + // we both have the rule, let's update some info then. | ||
| 181 | + flowAdded(rule); | ||
| 182 | + } else { | ||
| 183 | + // the device has a rule the store does not have | ||
| 184 | + extraneousFlow(rule); | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + for (FlowRule rule : storedRules) { | ||
| 188 | + // there are rules in the store that aren't on the switch | ||
| 189 | + flowMissing(rule); | ||
| 190 | + } | ||
| 166 | } | 191 | } |
| 167 | } | 192 | } |
| 168 | 193 | ... | ... |
| ... | @@ -6,6 +6,7 @@ import static org.junit.Assert.assertNotNull; | ... | @@ -6,6 +6,7 @@ import static org.junit.Assert.assertNotNull; |
| 6 | import static org.junit.Assert.assertTrue; | 6 | import static org.junit.Assert.assertTrue; |
| 7 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; | 7 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; |
| 8 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; | 8 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; |
| 9 | +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED; | ||
| 9 | 10 | ||
| 10 | import java.util.ArrayList; | 11 | import java.util.ArrayList; |
| 11 | import java.util.List; | 12 | import java.util.List; |
| ... | @@ -25,6 +26,7 @@ import org.onlab.onos.net.device.DeviceListener; | ... | @@ -25,6 +26,7 @@ import org.onlab.onos.net.device.DeviceListener; |
| 25 | import org.onlab.onos.net.device.DeviceService; | 26 | import org.onlab.onos.net.device.DeviceService; |
| 26 | import org.onlab.onos.net.flow.DefaultFlowRule; | 27 | import org.onlab.onos.net.flow.DefaultFlowRule; |
| 27 | import org.onlab.onos.net.flow.FlowRule; | 28 | import org.onlab.onos.net.flow.FlowRule; |
| 29 | +import org.onlab.onos.net.flow.FlowRule.FlowRuleState; | ||
| 28 | import org.onlab.onos.net.flow.FlowRuleEvent; | 30 | import org.onlab.onos.net.flow.FlowRuleEvent; |
| 29 | import org.onlab.onos.net.flow.FlowRuleListener; | 31 | import org.onlab.onos.net.flow.FlowRuleListener; |
| 30 | import org.onlab.onos.net.flow.FlowRuleProvider; | 32 | import org.onlab.onos.net.flow.FlowRuleProvider; |
| ... | @@ -37,10 +39,10 @@ import org.onlab.onos.net.flow.criteria.Criterion; | ... | @@ -37,10 +39,10 @@ import org.onlab.onos.net.flow.criteria.Criterion; |
| 37 | import org.onlab.onos.net.flow.instructions.Instruction; | 39 | import org.onlab.onos.net.flow.instructions.Instruction; |
| 38 | import org.onlab.onos.net.provider.AbstractProvider; | 40 | import org.onlab.onos.net.provider.AbstractProvider; |
| 39 | import org.onlab.onos.net.provider.ProviderId; | 41 | import org.onlab.onos.net.provider.ProviderId; |
| 42 | +import org.onlab.onos.net.trivial.impl.SimpleFlowRuleStore; | ||
| 40 | 43 | ||
| 41 | import com.google.common.collect.Lists; | 44 | import com.google.common.collect.Lists; |
| 42 | import com.google.common.collect.Sets; | 45 | import com.google.common.collect.Sets; |
| 43 | -import org.onlab.onos.net.trivial.impl.SimpleFlowRuleStore; | ||
| 44 | 46 | ||
| 45 | /** | 47 | /** |
| 46 | * Test codifying the flow rule service & flow rule provider service contracts. | 48 | * Test codifying the flow rule service & flow rule provider service contracts. |
| ... | @@ -56,7 +58,7 @@ public class FlowRuleManagerTest { | ... | @@ -56,7 +58,7 @@ public class FlowRuleManagerTest { |
| 56 | 58 | ||
| 57 | protected FlowRuleService service; | 59 | protected FlowRuleService service; |
| 58 | protected FlowRuleProviderRegistry registry; | 60 | protected FlowRuleProviderRegistry registry; |
| 59 | - protected FlowRuleProviderService providerSerivce; | 61 | + protected FlowRuleProviderService providerService; |
| 60 | protected TestProvider provider; | 62 | protected TestProvider provider; |
| 61 | protected TestListener listener = new TestListener(); | 63 | protected TestListener listener = new TestListener(); |
| 62 | 64 | ||
| ... | @@ -72,7 +74,7 @@ public class FlowRuleManagerTest { | ... | @@ -72,7 +74,7 @@ public class FlowRuleManagerTest { |
| 72 | mgr.activate(); | 74 | mgr.activate(); |
| 73 | mgr.addListener(listener); | 75 | mgr.addListener(listener); |
| 74 | provider = new TestProvider(PID); | 76 | provider = new TestProvider(PID); |
| 75 | - providerSerivce = registry.register(provider); | 77 | + providerService = registry.register(provider); |
| 76 | assertTrue("provider should be registered", | 78 | assertTrue("provider should be registered", |
| 77 | registry.getProviders().contains(provider.id())); | 79 | registry.getProviders().contains(provider.id())); |
| 78 | } | 80 | } |
| ... | @@ -94,10 +96,15 @@ public class FlowRuleManagerTest { | ... | @@ -94,10 +96,15 @@ public class FlowRuleManagerTest { |
| 94 | return new DefaultFlowRule(DID, ts, tr, 0); | 96 | return new DefaultFlowRule(DID, ts, tr, 0); |
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | - private void addFlowRule(int hval) { | 99 | + private FlowRule flowRule(FlowRule rule, FlowRuleState state) { |
| 100 | + return new DefaultFlowRule(rule, state); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + private FlowRule addFlowRule(int hval) { | ||
| 98 | FlowRule rule = flowRule(hval, hval); | 104 | FlowRule rule = flowRule(hval, hval); |
| 99 | - providerSerivce.flowAdded(rule); | 105 | + providerService.flowAdded(rule); |
| 100 | assertNotNull("rule should be found", service.getFlowEntries(DID)); | 106 | assertNotNull("rule should be found", service.getFlowEntries(DID)); |
| 107 | + return rule; | ||
| 101 | } | 108 | } |
| 102 | 109 | ||
| 103 | private void validateEvents(FlowRuleEvent.Type ... events) { | 110 | private void validateEvents(FlowRuleEvent.Type ... events) { |
| ... | @@ -131,60 +138,91 @@ public class FlowRuleManagerTest { | ... | @@ -131,60 +138,91 @@ public class FlowRuleManagerTest { |
| 131 | 138 | ||
| 132 | addFlowRule(1); | 139 | addFlowRule(1); |
| 133 | assertEquals("should still be 2 rules", 2, flowCount()); | 140 | assertEquals("should still be 2 rules", 2, flowCount()); |
| 134 | - validateEvents(); | 141 | + validateEvents(RULE_UPDATED); |
| 142 | + } | ||
| 143 | + | ||
| 144 | + | ||
| 145 | + //backing store is sensitive to the order of additions/removals | ||
| 146 | + private boolean validateState(FlowRuleState... state) { | ||
| 147 | + Iterable<FlowRule> rules = service.getFlowEntries(DID); | ||
| 148 | + int i = 0; | ||
| 149 | + for (FlowRule f : rules) { | ||
| 150 | + if (f.state() != state[i]) { | ||
| 151 | + return false; | ||
| 152 | + } | ||
| 153 | + i++; | ||
| 154 | + } | ||
| 155 | + return true; | ||
| 135 | } | 156 | } |
| 136 | 157 | ||
| 137 | @Test | 158 | @Test |
| 138 | public void applyFlowRules() { | 159 | public void applyFlowRules() { |
| 139 | - TestSelector ts = new TestSelector(1); | 160 | + |
| 140 | FlowRule r1 = flowRule(1, 1); | 161 | FlowRule r1 = flowRule(1, 1); |
| 141 | FlowRule r2 = flowRule(1, 2); | 162 | FlowRule r2 = flowRule(1, 2); |
| 142 | FlowRule r3 = flowRule(1, 3); | 163 | FlowRule r3 = flowRule(1, 3); |
| 143 | 164 | ||
| 144 | - //current FlowRules always return 0. FlowEntries inherit the value | ||
| 145 | - FlowRule e1 = new DefaultFlowRule(DID, ts, r1.treatment(), 0); | ||
| 146 | - FlowRule e2 = new DefaultFlowRule(DID, ts, r2.treatment(), 0); | ||
| 147 | - FlowRule e3 = new DefaultFlowRule(DID, ts, r3.treatment(), 0); | ||
| 148 | - List<FlowRule> fel = Lists.newArrayList(e1, e2, e3); | ||
| 149 | - | ||
| 150 | assertTrue("store should be empty", | 165 | assertTrue("store should be empty", |
| 151 | Sets.newHashSet(service.getFlowEntries(DID)).isEmpty()); | 166 | Sets.newHashSet(service.getFlowEntries(DID)).isEmpty()); |
| 152 | - List<FlowRule> ret = mgr.applyFlowRules(r1, r2, r3); | 167 | + mgr.applyFlowRules(r1, r2, r3); |
| 153 | assertEquals("3 rules should exist", 3, flowCount()); | 168 | assertEquals("3 rules should exist", 3, flowCount()); |
| 154 | - assertTrue("3 entries should result", fel.containsAll(ret)); | 169 | + assertTrue("Entries should be pending add.", |
| 170 | + validateState(FlowRuleState.PENDING_ADD, FlowRuleState.PENDING_ADD, | ||
| 171 | + FlowRuleState.PENDING_ADD)); | ||
| 155 | } | 172 | } |
| 156 | 173 | ||
| 157 | @Test | 174 | @Test |
| 158 | public void removeFlowRules() { | 175 | public void removeFlowRules() { |
| 159 | - addFlowRule(1); | 176 | + FlowRule f1 = addFlowRule(1); |
| 160 | - addFlowRule(2); | 177 | + FlowRule f2 = addFlowRule(2); |
| 161 | addFlowRule(3); | 178 | addFlowRule(3); |
| 162 | assertEquals("3 rules should exist", 3, flowCount()); | 179 | assertEquals("3 rules should exist", 3, flowCount()); |
| 163 | validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); | 180 | validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); |
| 164 | 181 | ||
| 165 | - FlowRule rem1 = flowRule(1, 1); | 182 | + FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED); |
| 166 | - FlowRule rem2 = flowRule(2, 2); | 183 | + FlowRule rem2 = flowRule(f2, FlowRuleState.REMOVED); |
| 167 | mgr.removeFlowRules(rem1, rem2); | 184 | mgr.removeFlowRules(rem1, rem2); |
| 168 | //removing from north, so no events generated | 185 | //removing from north, so no events generated |
| 169 | validateEvents(); | 186 | validateEvents(); |
| 170 | - assertEquals("1 rule should exist", 1, flowCount()); | 187 | + assertEquals("3 rule should exist", 3, flowCount()); |
| 188 | + assertTrue("Entries should be pending remove.", | ||
| 189 | + validateState(FlowRuleState.CREATED, FlowRuleState.PENDING_REMOVE, | ||
| 190 | + FlowRuleState.PENDING_REMOVE)); | ||
| 171 | 191 | ||
| 172 | mgr.removeFlowRules(rem1); | 192 | mgr.removeFlowRules(rem1); |
| 173 | - assertEquals("1 rule should still exist", 1, flowCount()); | 193 | + assertEquals("3 rule should still exist", 3, flowCount()); |
| 174 | } | 194 | } |
| 175 | 195 | ||
| 176 | @Test | 196 | @Test |
| 177 | public void flowRemoved() { | 197 | public void flowRemoved() { |
| 178 | - addFlowRule(1); | 198 | + FlowRule f1 = addFlowRule(1); |
| 179 | addFlowRule(2); | 199 | addFlowRule(2); |
| 180 | - FlowRule rem1 = flowRule(1, 1); | 200 | + FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED); |
| 181 | - providerSerivce.flowRemoved(rem1); | 201 | + providerService.flowRemoved(rem1); |
| 182 | validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); | 202 | validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); |
| 183 | 203 | ||
| 184 | - providerSerivce.flowRemoved(rem1); | 204 | + providerService.flowRemoved(rem1); |
| 185 | validateEvents(); | 205 | validateEvents(); |
| 186 | } | 206 | } |
| 187 | 207 | ||
| 208 | + @Test | ||
| 209 | + public void flowMetrics() { | ||
| 210 | + FlowRule f1 = flowRule(1, 1); | ||
| 211 | + FlowRule f2 = flowRule(2, 2); | ||
| 212 | + FlowRule f3 = flowRule(3, 3); | ||
| 213 | + | ||
| 214 | + FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED); | ||
| 215 | + FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED); | ||
| 216 | + mgr.applyFlowRules(f1, f2, f3); | ||
| 217 | + | ||
| 218 | + providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2)); | ||
| 219 | + | ||
| 220 | + assertTrue("Entries should be added.", | ||
| 221 | + validateState(FlowRuleState.PENDING_ADD, FlowRuleState.ADDED, | ||
| 222 | + FlowRuleState.ADDED)); | ||
| 223 | + //TODO: add tests for flowmissing and extraneous flows | ||
| 224 | + } | ||
| 225 | + | ||
| 188 | private static class TestListener implements FlowRuleListener { | 226 | private static class TestListener implements FlowRuleListener { |
| 189 | final List<FlowRuleEvent> events = new ArrayList<>(); | 227 | final List<FlowRuleEvent> events = new ArrayList<>(); |
| 190 | 228 | ... | ... |
| 1 | package org.onlab.onos.net.trivial.impl; | 1 | package org.onlab.onos.net.trivial.impl; |
| 2 | 2 | ||
| 3 | -import com.google.common.collect.HashMultimap; | 3 | +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; |
| 4 | -import com.google.common.collect.ImmutableSet; | 4 | +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; |
| 5 | -import com.google.common.collect.Multimap; | 5 | +import static org.slf4j.LoggerFactory.getLogger; |
| 6 | + | ||
| 6 | import org.apache.felix.scr.annotations.Activate; | 7 | import org.apache.felix.scr.annotations.Activate; |
| 7 | import org.apache.felix.scr.annotations.Component; | 8 | import org.apache.felix.scr.annotations.Component; |
| 8 | import org.apache.felix.scr.annotations.Deactivate; | 9 | import org.apache.felix.scr.annotations.Deactivate; |
| 9 | import org.apache.felix.scr.annotations.Service; | 10 | import org.apache.felix.scr.annotations.Service; |
| 10 | import org.onlab.onos.net.DeviceId; | 11 | import org.onlab.onos.net.DeviceId; |
| 11 | -import org.onlab.onos.net.flow.DefaultFlowRule; | ||
| 12 | import org.onlab.onos.net.flow.FlowRule; | 12 | import org.onlab.onos.net.flow.FlowRule; |
| 13 | import org.onlab.onos.net.flow.FlowRuleEvent; | 13 | import org.onlab.onos.net.flow.FlowRuleEvent; |
| 14 | +import org.onlab.onos.net.flow.FlowRuleEvent.Type; | ||
| 14 | import org.onlab.onos.net.flow.FlowRuleStore; | 15 | import org.onlab.onos.net.flow.FlowRuleStore; |
| 15 | import org.slf4j.Logger; | 16 | import org.slf4j.Logger; |
| 16 | 17 | ||
| 17 | -import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; | 18 | +import com.google.common.collect.ArrayListMultimap; |
| 18 | -import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; | 19 | +import com.google.common.collect.ImmutableSet; |
| 19 | -import static org.slf4j.LoggerFactory.getLogger; | 20 | +import com.google.common.collect.Multimap; |
| 20 | 21 | ||
| 21 | /** | 22 | /** |
| 22 | * Manages inventory of flow rules using trivial in-memory implementation. | 23 | * Manages inventory of flow rules using trivial in-memory implementation. |
| ... | @@ -28,7 +29,7 @@ public class SimpleFlowRuleStore implements FlowRuleStore { | ... | @@ -28,7 +29,7 @@ public class SimpleFlowRuleStore implements FlowRuleStore { |
| 28 | private final Logger log = getLogger(getClass()); | 29 | private final Logger log = getLogger(getClass()); |
| 29 | 30 | ||
| 30 | // store entries as a pile of rules, no info about device tables | 31 | // store entries as a pile of rules, no info about device tables |
| 31 | - private final Multimap<DeviceId, FlowRule> flowEntries = HashMultimap.create(); | 32 | + private final Multimap<DeviceId, FlowRule> flowEntries = ArrayListMultimap.create(); |
| 32 | 33 | ||
| 33 | @Activate | 34 | @Activate |
| 34 | public void activate() { | 35 | public void activate() { |
| ... | @@ -46,12 +47,27 @@ public class SimpleFlowRuleStore implements FlowRuleStore { | ... | @@ -46,12 +47,27 @@ public class SimpleFlowRuleStore implements FlowRuleStore { |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | @Override | 49 | @Override |
| 49 | - public FlowRule storeFlowRule(FlowRule rule) { | 50 | + public void storeFlowRule(FlowRule rule) { |
| 51 | + DeviceId did = rule.deviceId(); | ||
| 52 | + flowEntries.put(did, rule); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public void deleteFlowRule(FlowRule rule) { | ||
| 50 | DeviceId did = rule.deviceId(); | 57 | DeviceId did = rule.deviceId(); |
| 51 | - FlowRule entry = new DefaultFlowRule(did, | 58 | + |
| 52 | - rule.selector(), rule.treatment(), rule.priority()); | 59 | + /* |
| 53 | - flowEntries.put(did, entry); | 60 | + * find the rule and mark it for deletion. |
| 54 | - return entry; | 61 | + * Ultimately a flow removed will come remove it. |
| 62 | + */ | ||
| 63 | + | ||
| 64 | + if (flowEntries.containsEntry(did, rule)) { | ||
| 65 | + synchronized (flowEntries) { | ||
| 66 | + | ||
| 67 | + flowEntries.remove(did, rule); | ||
| 68 | + flowEntries.put(did, rule); | ||
| 69 | + } | ||
| 70 | + } | ||
| 55 | } | 71 | } |
| 56 | 72 | ||
| 57 | @Override | 73 | @Override |
| ... | @@ -59,12 +75,17 @@ public class SimpleFlowRuleStore implements FlowRuleStore { | ... | @@ -59,12 +75,17 @@ public class SimpleFlowRuleStore implements FlowRuleStore { |
| 59 | DeviceId did = rule.deviceId(); | 75 | DeviceId did = rule.deviceId(); |
| 60 | 76 | ||
| 61 | // check if this new rule is an update to an existing entry | 77 | // check if this new rule is an update to an existing entry |
| 62 | - for (FlowRule fe : flowEntries.get(did)) { | 78 | + if (flowEntries.containsEntry(did, rule)) { |
| 63 | - if (rule.equals(fe)) { | 79 | + synchronized (flowEntries) { |
| 64 | - // TODO update the stats on this FlowRule? | 80 | + // Multimaps support duplicates so we have to remove our rule |
| 65 | - return null; | 81 | + // and replace it with the current version. |
| 82 | + | ||
| 83 | + flowEntries.remove(did, rule); | ||
| 84 | + flowEntries.put(did, rule); | ||
| 66 | } | 85 | } |
| 86 | + return new FlowRuleEvent(Type.RULE_UPDATED, rule); | ||
| 67 | } | 87 | } |
| 88 | + | ||
| 68 | flowEntries.put(did, rule); | 89 | flowEntries.put(did, rule); |
| 69 | return new FlowRuleEvent(RULE_ADDED, rule); | 90 | return new FlowRuleEvent(RULE_ADDED, rule); |
| 70 | } | 91 | } |
| ... | @@ -80,4 +101,6 @@ public class SimpleFlowRuleStore implements FlowRuleStore { | ... | @@ -80,4 +101,6 @@ public class SimpleFlowRuleStore implements FlowRuleStore { |
| 80 | } | 101 | } |
| 81 | } | 102 | } |
| 82 | 103 | ||
| 104 | + | ||
| 105 | + | ||
| 83 | } | 106 | } | ... | ... |
| ... | @@ -88,6 +88,24 @@ public class FlowModBuilder { | ... | @@ -88,6 +88,24 @@ public class FlowModBuilder { |
| 88 | 88 | ||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | + public OFFlowMod buildFlowDel() { | ||
| 92 | + Match match = buildMatch(); | ||
| 93 | + List<OFAction> actions = buildActions(); | ||
| 94 | + | ||
| 95 | + OFFlowMod fm = factory.buildFlowDelete() | ||
| 96 | + .setCookie(U64.of(cookie.value())) | ||
| 97 | + .setBufferId(OFBufferId.NO_BUFFER) | ||
| 98 | + .setActions(actions) | ||
| 99 | + .setMatch(match) | ||
| 100 | + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) | ||
| 101 | + .setIdleTimeout(10) | ||
| 102 | + .setHardTimeout(10) | ||
| 103 | + .setPriority(priority) | ||
| 104 | + .build(); | ||
| 105 | + | ||
| 106 | + return fm; | ||
| 107 | + } | ||
| 108 | + | ||
| 91 | private List<OFAction> buildActions() { | 109 | private List<OFAction> buildActions() { |
| 92 | List<OFAction> acts = new LinkedList<>(); | 110 | List<OFAction> acts = new LinkedList<>(); |
| 93 | for (Instruction i : treatment.instructions()) { | 111 | for (Instruction i : treatment.instructions()) { |
| ... | @@ -246,4 +264,6 @@ public class FlowModBuilder { | ... | @@ -246,4 +264,6 @@ public class FlowModBuilder { |
| 246 | return mBuilder.build(); | 264 | return mBuilder.build(); |
| 247 | } | 265 | } |
| 248 | 266 | ||
| 267 | + | ||
| 268 | + | ||
| 249 | } | 269 | } | ... | ... |
| ... | @@ -10,6 +10,7 @@ import org.onlab.onos.net.flow.DefaultFlowRule; | ... | @@ -10,6 +10,7 @@ import org.onlab.onos.net.flow.DefaultFlowRule; |
| 10 | import org.onlab.onos.net.flow.DefaultTrafficSelector; | 10 | import org.onlab.onos.net.flow.DefaultTrafficSelector; |
| 11 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; | 11 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; |
| 12 | import org.onlab.onos.net.flow.FlowRule; | 12 | import org.onlab.onos.net.flow.FlowRule; |
| 13 | +import org.onlab.onos.net.flow.FlowRule.FlowRuleState; | ||
| 13 | import org.onlab.onos.net.flow.TrafficSelector; | 14 | import org.onlab.onos.net.flow.TrafficSelector; |
| 14 | import org.onlab.onos.net.flow.TrafficTreatment; | 15 | import org.onlab.onos.net.flow.TrafficTreatment; |
| 15 | import org.onlab.onos.net.flow.criteria.Criteria; | 16 | import org.onlab.onos.net.flow.criteria.Criteria; |
| ... | @@ -52,7 +53,7 @@ public class FlowRuleBuilder { | ... | @@ -52,7 +53,7 @@ public class FlowRuleBuilder { |
| 52 | this.match = entry.getMatch(); | 53 | this.match = entry.getMatch(); |
| 53 | this.actions = entry.getActions(); | 54 | this.actions = entry.getActions(); |
| 54 | this.dpid = dpid; | 55 | this.dpid = dpid; |
| 55 | - removed = null; | 56 | + this.removed = null; |
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | public FlowRuleBuilder(Dpid dpid, OFFlowRemoved removed) { | 59 | public FlowRuleBuilder(Dpid dpid, OFFlowRemoved removed) { |
| ... | @@ -69,16 +70,16 @@ public class FlowRuleBuilder { | ... | @@ -69,16 +70,16 @@ public class FlowRuleBuilder { |
| 69 | if (stat != null) { | 70 | if (stat != null) { |
| 70 | return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), | 71 | return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), |
| 71 | buildSelector(), buildTreatment(), stat.getPriority(), | 72 | buildSelector(), buildTreatment(), stat.getPriority(), |
| 72 | - stat.getDurationNsec() / 1000000, stat.getIdleTimeout(), | 73 | + FlowRuleState.ADDED, stat.getDurationNsec() / 1000000, |
| 73 | stat.getPacketCount().getValue(), stat.getByteCount().getValue(), | 74 | stat.getPacketCount().getValue(), stat.getByteCount().getValue(), |
| 74 | - (int) (stat.getCookie().getValue() & 0xFFFFFFFF)); | 75 | + stat.getCookie().getValue()); |
| 75 | } else { | 76 | } else { |
| 76 | // TODO: revisit potentially. | 77 | // TODO: revisit potentially. |
| 77 | return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), | 78 | return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), |
| 78 | buildSelector(), null, removed.getPriority(), | 79 | buildSelector(), null, removed.getPriority(), |
| 79 | - removed.getDurationNsec() / 1000000, removed.getIdleTimeout(), | 80 | + FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000, |
| 80 | removed.getPacketCount().getValue(), removed.getByteCount().getValue(), | 81 | removed.getPacketCount().getValue(), removed.getByteCount().getValue(), |
| 81 | - (int) (removed.getCookie().getValue() & 0xFFFFFFFF)); | 82 | + removed.getCookie().getValue()); |
| 82 | } | 83 | } |
| 83 | } | 84 | } |
| 84 | 85 | ... | ... |
providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
| ... | @@ -10,6 +10,7 @@ import org.apache.felix.scr.annotations.Component; | ... | @@ -10,6 +10,7 @@ import org.apache.felix.scr.annotations.Component; |
| 10 | import org.apache.felix.scr.annotations.Deactivate; | 10 | import org.apache.felix.scr.annotations.Deactivate; |
| 11 | import org.apache.felix.scr.annotations.Reference; | 11 | import org.apache.felix.scr.annotations.Reference; |
| 12 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 12 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 13 | +import org.onlab.onos.net.DeviceId; | ||
| 13 | import org.onlab.onos.net.flow.FlowRule; | 14 | import org.onlab.onos.net.flow.FlowRule; |
| 14 | import org.onlab.onos.net.flow.FlowRuleProvider; | 15 | import org.onlab.onos.net.flow.FlowRuleProvider; |
| 15 | import org.onlab.onos.net.flow.FlowRuleProviderRegistry; | 16 | import org.onlab.onos.net.flow.FlowRuleProviderRegistry; |
| ... | @@ -94,8 +95,16 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -94,8 +95,16 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
| 94 | 95 | ||
| 95 | @Override | 96 | @Override |
| 96 | public void removeFlowRule(FlowRule... flowRules) { | 97 | public void removeFlowRule(FlowRule... flowRules) { |
| 97 | - // TODO Auto-generated method stub | 98 | + for (int i = 0; i < flowRules.length; i++) { |
| 99 | + removeRule(flowRules[i]); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + } | ||
| 103 | + | ||
| 98 | 104 | ||
| 105 | + private void removeRule(FlowRule flowRule) { | ||
| 106 | + OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); | ||
| 107 | + sw.sendMsg(new FlowModBuilder(flowRule, sw.factory()).buildFlowDel()); | ||
| 99 | } | 108 | } |
| 100 | 109 | ||
| 101 | 110 | ||
| ... | @@ -108,7 +117,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -108,7 +117,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
| 108 | 117 | ||
| 109 | @Override | 118 | @Override |
| 110 | public void switchAdded(Dpid dpid) { | 119 | public void switchAdded(Dpid dpid) { |
| 111 | - FlowStatsCollector fsc = new FlowStatsCollector(controller.getSwitch(dpid), 1); | 120 | + FlowStatsCollector fsc = new FlowStatsCollector(controller.getSwitch(dpid), 5); |
| 112 | fsc.start(); | 121 | fsc.start(); |
| 113 | collectors.put(dpid, fsc); | 122 | collectors.put(dpid, fsc); |
| 114 | } | 123 | } |
| ... | @@ -154,7 +163,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -154,7 +163,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
| 154 | entries.add(new FlowRuleBuilder(dpid, reply).build()); | 163 | entries.add(new FlowRuleBuilder(dpid, reply).build()); |
| 155 | } | 164 | } |
| 156 | log.debug("sending flowstats to core {}", entries); | 165 | log.debug("sending flowstats to core {}", entries); |
| 157 | - providerService.pushFlowMetrics(entries); | 166 | + providerService.pushFlowMetrics(DeviceId.deviceId(Dpid.uri(dpid)), entries); |
| 158 | } | 167 | } |
| 159 | 168 | ||
| 160 | } | 169 | } | ... | ... |
| 1 | +package org.onlab.packet; | ||
| 2 | + | ||
| 3 | +import java.util.Arrays; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * A class representing an IPv4 address. | ||
| 7 | + * <p/> | ||
| 8 | + * TODO this class is a clone of IpPrefix and still needs to be modified to | ||
| 9 | + * look more like an IpAddress. | ||
| 10 | + */ | ||
| 11 | +public final class IpAddress { | ||
| 12 | + | ||
| 13 | + // TODO a comparator for netmasks? E.g. for sorting by prefix match order. | ||
| 14 | + | ||
| 15 | + //IP Versions | ||
| 16 | + public enum Version { INET, INET6 }; | ||
| 17 | + | ||
| 18 | + //lengths of address, in bytes | ||
| 19 | + public static final int INET_LEN = 4; | ||
| 20 | + public static final int INET6_LEN = 16; | ||
| 21 | + | ||
| 22 | + //maximum CIDR value | ||
| 23 | + public static final int MAX_INET_MASK = 32; | ||
| 24 | + //no mask (no network), e.g. a simple address | ||
| 25 | + public static final int DEFAULT_MASK = 0; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * Default value indicating an unspecified address. | ||
| 29 | + */ | ||
| 30 | + static final byte[] ANY = new byte [] {0, 0, 0, 0}; | ||
| 31 | + | ||
| 32 | + protected Version version; | ||
| 33 | + | ||
| 34 | + protected byte[] octets; | ||
| 35 | + protected int netmask; | ||
| 36 | + | ||
| 37 | + private IpAddress(Version ver, byte[] octets, int netmask) { | ||
| 38 | + this.version = ver; | ||
| 39 | + this.octets = Arrays.copyOf(octets, INET_LEN); | ||
| 40 | + this.netmask = netmask; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + private IpAddress(Version ver, byte[] octets) { | ||
| 44 | + this.version = ver; | ||
| 45 | + this.octets = Arrays.copyOf(octets, INET_LEN); | ||
| 46 | + this.netmask = DEFAULT_MASK; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * Converts a byte array into an IP address. | ||
| 51 | + * | ||
| 52 | + * @param address a byte array | ||
| 53 | + * @return an IP address | ||
| 54 | + */ | ||
| 55 | + public static IpAddress valueOf(byte [] address) { | ||
| 56 | + return new IpAddress(Version.INET, address); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * Converts a byte array into an IP address. | ||
| 61 | + * | ||
| 62 | + * @param address a byte array | ||
| 63 | + * @param netmask the CIDR value subnet mask | ||
| 64 | + * @return an IP address | ||
| 65 | + */ | ||
| 66 | + public static IpAddress valueOf(byte [] address, int netmask) { | ||
| 67 | + return new IpAddress(Version.INET, address, netmask); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * Helper to convert an integer into a byte array. | ||
| 72 | + * | ||
| 73 | + * @param address the integer to convert | ||
| 74 | + * @return a byte array | ||
| 75 | + */ | ||
| 76 | + private static byte [] bytes(int address) { | ||
| 77 | + byte [] bytes = new byte [INET_LEN]; | ||
| 78 | + for (int i = 0; i < INET_LEN; i++) { | ||
| 79 | + bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + return bytes; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + /** | ||
| 86 | + * Converts an integer into an IPv4 address. | ||
| 87 | + * | ||
| 88 | + * @param address an integer representing an IP value | ||
| 89 | + * @return an IP address | ||
| 90 | + */ | ||
| 91 | + public static IpAddress valueOf(int address) { | ||
| 92 | + return new IpAddress(Version.INET, bytes(address)); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + /** | ||
| 96 | + * Converts an integer into an IPv4 address. | ||
| 97 | + * | ||
| 98 | + * @param address an integer representing an IP value | ||
| 99 | + * @param netmask the CIDR value subnet mask | ||
| 100 | + * @return an IP address | ||
| 101 | + */ | ||
| 102 | + public static IpAddress valueOf(int address, int netmask) { | ||
| 103 | + return new IpAddress(Version.INET, bytes(address), netmask); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The | ||
| 108 | + * string can also be in CIDR (slash) notation. If the netmask is omitted, | ||
| 109 | + * it will be set to DEFAULT_MASK (0). | ||
| 110 | + * | ||
| 111 | + * @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24" | ||
| 112 | + * @return an IP address | ||
| 113 | + */ | ||
| 114 | + public static IpAddress valueOf(String address) { | ||
| 115 | + | ||
| 116 | + final String [] parts = address.split("\\/"); | ||
| 117 | + if (parts.length > 2) { | ||
| 118 | + throw new IllegalArgumentException("Malformed IP address string; " | ||
| 119 | + + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\""); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + int mask = DEFAULT_MASK; | ||
| 123 | + if (parts.length == 2) { | ||
| 124 | + mask = Integer.valueOf(parts[1]); | ||
| 125 | + if (mask > MAX_INET_MASK) { | ||
| 126 | + throw new IllegalArgumentException( | ||
| 127 | + "Value of subnet mask cannot exceed " | ||
| 128 | + + MAX_INET_MASK); | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + final String [] net = parts[0].split("\\."); | ||
| 133 | + if (net.length != INET_LEN) { | ||
| 134 | + throw new IllegalArgumentException("Malformed IP address string; " | ||
| 135 | + + "Address must have four decimal values separated by dots (.)"); | ||
| 136 | + } | ||
| 137 | + final byte [] bytes = new byte[INET_LEN]; | ||
| 138 | + for (int i = 0; i < INET_LEN; i++) { | ||
| 139 | + bytes[i] = (byte) Short.parseShort(net[i], 10); | ||
| 140 | + } | ||
| 141 | + return new IpAddress(Version.INET, bytes, mask); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * Returns the IP version of this address. | ||
| 146 | + * | ||
| 147 | + * @return the version | ||
| 148 | + */ | ||
| 149 | + public Version version() { | ||
| 150 | + return this.version; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + /** | ||
| 154 | + * Returns the IP address as a byte array. | ||
| 155 | + * | ||
| 156 | + * @return a byte array | ||
| 157 | + */ | ||
| 158 | + public byte[] toOctets() { | ||
| 159 | + return Arrays.copyOf(this.octets, INET_LEN); | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + /** | ||
| 163 | + * Returns the IP address prefix length. | ||
| 164 | + * | ||
| 165 | + * @return prefix length | ||
| 166 | + */ | ||
| 167 | + public int prefixLength() { | ||
| 168 | + return netmask; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + /** | ||
| 172 | + * Returns the integral value of this IP address. | ||
| 173 | + * | ||
| 174 | + * @return the IP address's value as an integer | ||
| 175 | + */ | ||
| 176 | + public int toInt() { | ||
| 177 | + int address = 0; | ||
| 178 | + for (int i = 0; i < INET_LEN; i++) { | ||
| 179 | + address |= octets[i] << ((INET_LEN - (i + 1)) * 8); | ||
| 180 | + } | ||
| 181 | + return address; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + /** | ||
| 185 | + * Helper for computing the mask value from CIDR. | ||
| 186 | + * | ||
| 187 | + * @return an integer bitmask | ||
| 188 | + */ | ||
| 189 | + private int mask() { | ||
| 190 | + int shift = MAX_INET_MASK - this.netmask; | ||
| 191 | + return ((Integer.MAX_VALUE >>> (shift - 1)) << shift); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + /** | ||
| 195 | + * Returns the subnet mask in IpAddress form. The netmask value for | ||
| 196 | + * the returned IpAddress is 0, as the address itself is a mask. | ||
| 197 | + * | ||
| 198 | + * @return the subnet mask | ||
| 199 | + */ | ||
| 200 | + public IpAddress netmask() { | ||
| 201 | + return new IpAddress(Version.INET, bytes(mask())); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + /** | ||
| 205 | + * Returns the network portion of this address as an IpAddress. | ||
| 206 | + * The netmask of the returned IpAddress is the current mask. If this | ||
| 207 | + * address doesn't have a mask, this returns an all-0 IpAddress. | ||
| 208 | + * | ||
| 209 | + * @return the network address or null | ||
| 210 | + */ | ||
| 211 | + public IpAddress network() { | ||
| 212 | + if (netmask == DEFAULT_MASK) { | ||
| 213 | + return new IpAddress(version, ANY, DEFAULT_MASK); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + byte [] net = new byte [4]; | ||
| 217 | + byte [] mask = bytes(mask()); | ||
| 218 | + for (int i = 0; i < INET_LEN; i++) { | ||
| 219 | + net[i] = (byte) (octets[i] & mask[i]); | ||
| 220 | + } | ||
| 221 | + return new IpAddress(version, net, netmask); | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + /** | ||
| 225 | + * Returns the host portion of the IPAddress, as an IPAddress. | ||
| 226 | + * The netmask of the returned IpAddress is the current mask. If this | ||
| 227 | + * address doesn't have a mask, this returns a copy of the current | ||
| 228 | + * address. | ||
| 229 | + * | ||
| 230 | + * @return the host address | ||
| 231 | + */ | ||
| 232 | + public IpAddress host() { | ||
| 233 | + if (netmask == DEFAULT_MASK) { | ||
| 234 | + new IpAddress(version, octets, netmask); | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + byte [] host = new byte [INET_LEN]; | ||
| 238 | + byte [] mask = bytes(mask()); | ||
| 239 | + for (int i = 0; i < INET_LEN; i++) { | ||
| 240 | + host[i] = (byte) (octets[i] & ~mask[i]); | ||
| 241 | + } | ||
| 242 | + return new IpAddress(version, host, netmask); | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + public boolean isMasked() { | ||
| 246 | + return mask() != 0; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + /** | ||
| 250 | + * Determines whether a given address is contained within this IpAddress' | ||
| 251 | + * network. | ||
| 252 | + * | ||
| 253 | + * @param other another IP address that could be contained in this network | ||
| 254 | + * @return true if the other IP address is contained in this address' | ||
| 255 | + * network, otherwise false | ||
| 256 | + */ | ||
| 257 | + public boolean contains(IpAddress other) { | ||
| 258 | + if (this.netmask <= other.netmask) { | ||
| 259 | + // Special case where they're both /32 addresses | ||
| 260 | + if (this.netmask == MAX_INET_MASK) { | ||
| 261 | + return Arrays.equals(octets, other.octets); | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + // Mask the other address with our network mask | ||
| 265 | + IpAddress otherMasked = | ||
| 266 | + IpAddress.valueOf(other.octets, netmask).network(); | ||
| 267 | + | ||
| 268 | + return network().equals(otherMasked); | ||
| 269 | + } | ||
| 270 | + return false; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + @Override | ||
| 274 | + public int hashCode() { | ||
| 275 | + final int prime = 31; | ||
| 276 | + int result = 1; | ||
| 277 | + result = prime * result + netmask; | ||
| 278 | + result = prime * result + Arrays.hashCode(octets); | ||
| 279 | + result = prime * result + ((version == null) ? 0 : version.hashCode()); | ||
| 280 | + return result; | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + @Override | ||
| 284 | + public boolean equals(Object obj) { | ||
| 285 | + if (this == obj) { | ||
| 286 | + return true; | ||
| 287 | + } | ||
| 288 | + if (obj == null) { | ||
| 289 | + return false; | ||
| 290 | + } | ||
| 291 | + if (getClass() != obj.getClass()) { | ||
| 292 | + return false; | ||
| 293 | + } | ||
| 294 | + IpAddress other = (IpAddress) obj; | ||
| 295 | + if (netmask != other.netmask) { | ||
| 296 | + return false; | ||
| 297 | + } | ||
| 298 | + if (!Arrays.equals(octets, other.octets)) { | ||
| 299 | + return false; | ||
| 300 | + } | ||
| 301 | + if (version != other.version) { | ||
| 302 | + return false; | ||
| 303 | + } | ||
| 304 | + return true; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + @Override | ||
| 308 | + /* | ||
| 309 | + * (non-Javadoc) | ||
| 310 | + * format is "x.x.x.x" for non-masked (netmask 0) addresses, | ||
| 311 | + * and "x.x.x.x/y" for masked addresses. | ||
| 312 | + * | ||
| 313 | + * @see java.lang.Object#toString() | ||
| 314 | + */ | ||
| 315 | + public String toString() { | ||
| 316 | + final StringBuilder builder = new StringBuilder(); | ||
| 317 | + for (final byte b : this.octets) { | ||
| 318 | + if (builder.length() > 0) { | ||
| 319 | + builder.append("."); | ||
| 320 | + } | ||
| 321 | + builder.append(String.format("%d", b & 0xff)); | ||
| 322 | + } | ||
| 323 | + if (netmask != DEFAULT_MASK) { | ||
| 324 | + builder.append("/"); | ||
| 325 | + builder.append(String.format("%d", netmask)); | ||
| 326 | + } | ||
| 327 | + return builder.toString(); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | +} |
| ... | @@ -3,7 +3,9 @@ package org.onlab.packet; | ... | @@ -3,7 +3,9 @@ package org.onlab.packet; |
| 3 | import java.util.Arrays; | 3 | import java.util.Arrays; |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
| 6 | - * A class representing an IPv4 address. | 6 | + * A class representing an IPv4 prefix. |
| 7 | + * <p/> | ||
| 8 | + * A prefix consists of an IP address and a subnet mask. | ||
| 7 | */ | 9 | */ |
| 8 | public final class IpPrefix { | 10 | public final class IpPrefix { |
| 9 | 11 | ... | ... |
-
Please register or login to post a comment