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