Committed by
Gerrit Code Review
CORD-13:Table Statistics support along with CLI and REST
Change-Id: Ic7facc73754c4b1e7c9c5a9eecd217f89a67e135
Showing
18 changed files
with
721 additions
and
9 deletions
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.cli.net; | ||
17 | + | ||
18 | +import static com.google.common.collect.Lists.newArrayList; | ||
19 | + | ||
20 | +import java.util.Collections; | ||
21 | +import java.util.List; | ||
22 | +import java.util.Map; | ||
23 | +import java.util.SortedMap; | ||
24 | +import java.util.TreeMap; | ||
25 | + | ||
26 | +import org.apache.karaf.shell.commands.Argument; | ||
27 | +import org.apache.karaf.shell.commands.Command; | ||
28 | +import org.apache.karaf.shell.commands.Option; | ||
29 | +import org.onosproject.cli.AbstractShellCommand; | ||
30 | +import org.onosproject.cli.Comparators; | ||
31 | +import org.onosproject.net.Device; | ||
32 | +import org.onosproject.net.DeviceId; | ||
33 | +import org.onosproject.net.device.DeviceService; | ||
34 | +import org.onosproject.net.flow.FlowRuleService; | ||
35 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
36 | + | ||
37 | +import com.fasterxml.jackson.databind.JsonNode; | ||
38 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
39 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
40 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
41 | + | ||
42 | +/** | ||
43 | + * Lists port statistic of all ports in the system. | ||
44 | + */ | ||
45 | +@Command(scope = "onos", name = "tablestats", | ||
46 | + description = "Lists statistics of all tables in the device") | ||
47 | +public class TableStatisticsCommand extends AbstractShellCommand { | ||
48 | + | ||
49 | + @Option(name = "-t", aliases = "--table", description = "Show human readable table format for statistics", | ||
50 | + required = false, multiValued = false) | ||
51 | + private boolean table = false; | ||
52 | + | ||
53 | + @Argument(index = 0, name = "uri", description = "Device ID", | ||
54 | + required = false, multiValued = false) | ||
55 | + String uri = null; | ||
56 | + | ||
57 | + private static final String FORMAT = | ||
58 | + " table=%s, active=%s, lookedup=%s, matched=%s"; | ||
59 | + | ||
60 | + @Override | ||
61 | + protected void execute() { | ||
62 | + FlowRuleService flowService = get(FlowRuleService.class); | ||
63 | + DeviceService deviceService = get(DeviceService.class); | ||
64 | + | ||
65 | + SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = | ||
66 | + getSortedTableStats(deviceService, flowService); | ||
67 | + | ||
68 | + if (outputJson()) { | ||
69 | + print("%s", json(deviceTableStats.keySet(), deviceTableStats)); | ||
70 | + } else { | ||
71 | + deviceTableStats.forEach((device, tableStats) -> printTableStats(device, tableStats)); | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + /** | ||
76 | + * Produces a JSON array of table statistics grouped by the each device. | ||
77 | + * | ||
78 | + * @param devices collection of devices | ||
79 | + * @param deviceTableStats collection of table statistics per each device | ||
80 | + * @return JSON array | ||
81 | + */ | ||
82 | + private JsonNode json(Iterable<Device> devices, | ||
83 | + Map<Device, List<TableStatisticsEntry>> deviceTableStats) { | ||
84 | + ObjectMapper mapper = new ObjectMapper(); | ||
85 | + ArrayNode result = mapper.createArrayNode(); | ||
86 | + for (Device device : devices) { | ||
87 | + result.add(json(mapper, device, deviceTableStats.get(device))); | ||
88 | + } | ||
89 | + return result; | ||
90 | + } | ||
91 | + | ||
92 | + // Produces JSON object with the table statistics of the given device. | ||
93 | + private ObjectNode json(ObjectMapper mapper, | ||
94 | + Device device, List<TableStatisticsEntry> tableStats) { | ||
95 | + ObjectNode result = mapper.createObjectNode(); | ||
96 | + ArrayNode array = mapper.createArrayNode(); | ||
97 | + | ||
98 | + tableStats.forEach(tableStat -> array.add(jsonForEntity(tableStat, TableStatisticsEntry.class))); | ||
99 | + | ||
100 | + result.put("device", device.id().toString()) | ||
101 | + .put("tableCount", tableStats.size()) | ||
102 | + .set("tables", array); | ||
103 | + return result; | ||
104 | + } | ||
105 | + | ||
106 | + /** | ||
107 | + * Prints flow table statistics. | ||
108 | + * | ||
109 | + * @param d the device | ||
110 | + * @param tableStats the set of flow table statistics for that device | ||
111 | + */ | ||
112 | + protected void printTableStats(Device d, | ||
113 | + List<TableStatisticsEntry> tableStats) { | ||
114 | + boolean empty = tableStats == null || tableStats.isEmpty(); | ||
115 | + print("deviceId=%s, tableCount=%d", d.id(), empty ? 0 : tableStats.size()); | ||
116 | + if (!empty) { | ||
117 | + for (TableStatisticsEntry t : tableStats) { | ||
118 | + print(FORMAT, t.tableId(), t.activeFlowEntries(), | ||
119 | + t.packetsLookedup(), t.packetsMatched()); | ||
120 | + } | ||
121 | + } | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Returns the list of table statistics sorted using the device ID URIs and table IDs. | ||
126 | + * | ||
127 | + * @param deviceService device service | ||
128 | + * @param flowService flow rule service | ||
129 | + * @return sorted table statistics list | ||
130 | + */ | ||
131 | + protected SortedMap<Device, List<TableStatisticsEntry>> getSortedTableStats(DeviceService deviceService, | ||
132 | + FlowRuleService flowService) { | ||
133 | + SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = new TreeMap<>(Comparators.ELEMENT_COMPARATOR); | ||
134 | + List<TableStatisticsEntry> tableStatsList; | ||
135 | + Iterable<Device> devices = uri == null ? deviceService.getDevices() : | ||
136 | + Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri))); | ||
137 | + for (Device d : devices) { | ||
138 | + tableStatsList = newArrayList(flowService.getFlowTableStatistics(d.id())); | ||
139 | + tableStatsList.sort((p1, p2) -> Integer.valueOf(p1.tableId()).compareTo(Integer.valueOf(p2.tableId()))); | ||
140 | + deviceTableStats.put(d, tableStatsList); | ||
141 | + } | ||
142 | + return deviceTableStats; | ||
143 | + } | ||
144 | + | ||
145 | +} |
... | @@ -348,6 +348,10 @@ | ... | @@ -348,6 +348,10 @@ |
348 | </command> | 348 | </command> |
349 | 349 | ||
350 | <command> | 350 | <command> |
351 | + <action class="org.onosproject.cli.net.TableStatisticsCommand"/> | ||
352 | + </command> | ||
353 | + | ||
354 | + <command> | ||
351 | <action class="org.onosproject.cli.net.FlowsListCommand"/> | 355 | <action class="org.onosproject.cli.net.FlowsListCommand"/> |
352 | <completers> | 356 | <completers> |
353 | <ref component-id="flowRuleStatusCompleter"/> | 357 | <ref component-id="flowRuleStatusCompleter"/> | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.net.flow; | ||
17 | + | ||
18 | +import org.onosproject.net.DeviceId; | ||
19 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
20 | + | ||
21 | +/** | ||
22 | + * Default implementation of table statistics entry interface. | ||
23 | + */ | ||
24 | +public final class DefaultTableStatisticsEntry implements TableStatisticsEntry { | ||
25 | + | ||
26 | + private final DeviceId deviceId; | ||
27 | + private final int tableId; | ||
28 | + private final long activeFlowEntries; | ||
29 | + private final long packetsLookedupCount; | ||
30 | + private final long packetsMatchedCount; | ||
31 | + | ||
32 | + /** | ||
33 | + * Default table statistics constructor. | ||
34 | + * | ||
35 | + * @param deviceId device identifier | ||
36 | + * @param tableId table identifier | ||
37 | + * @param activeFlowEntries number of active flow entries in the table | ||
38 | + * @param packetsLookedupCount number of packets looked up in table | ||
39 | + * @param packetsMatchedCount number of packets that hit table | ||
40 | + */ | ||
41 | + public DefaultTableStatisticsEntry(DeviceId deviceId, | ||
42 | + int tableId, | ||
43 | + long activeFlowEntries, | ||
44 | + long packetsLookedupCount, | ||
45 | + long packetsMatchedCount) { | ||
46 | + this.deviceId = checkNotNull(deviceId); | ||
47 | + this.tableId = tableId; | ||
48 | + this.activeFlowEntries = activeFlowEntries; | ||
49 | + this.packetsLookedupCount = packetsLookedupCount; | ||
50 | + this.packetsMatchedCount = packetsMatchedCount; | ||
51 | + } | ||
52 | + | ||
53 | + @Override | ||
54 | + public String toString() { | ||
55 | + StringBuilder sb = new StringBuilder("device: " + deviceId + ", "); | ||
56 | + | ||
57 | + sb.append("tableId: " + this.tableId + ", "); | ||
58 | + sb.append("activeEntries: " + this.activeFlowEntries + ", "); | ||
59 | + sb.append("packetsLookedUp: " + this.packetsLookedupCount + ", "); | ||
60 | + sb.append("packetsMatched: " + this.packetsMatchedCount); | ||
61 | + | ||
62 | + return sb.toString(); | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + public int tableId() { | ||
67 | + return tableId; | ||
68 | + } | ||
69 | + | ||
70 | + @Override | ||
71 | + public long activeFlowEntries() { | ||
72 | + return activeFlowEntries; | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public long packetsLookedup() { | ||
77 | + return packetsLookedupCount; | ||
78 | + } | ||
79 | + | ||
80 | + @Override | ||
81 | + public long packetsMatched() { | ||
82 | + return packetsMatchedCount; | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public DeviceId deviceId() { | ||
87 | + return deviceId; | ||
88 | + } | ||
89 | +} |
... | @@ -15,6 +15,8 @@ | ... | @@ -15,6 +15,8 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.net.flow; | 16 | package org.onosproject.net.flow; |
17 | 17 | ||
18 | +import java.util.List; | ||
19 | + | ||
18 | import org.onosproject.net.DeviceId; | 20 | import org.onosproject.net.DeviceId; |
19 | import org.onosproject.net.provider.ProviderService; | 21 | import org.onosproject.net.provider.ProviderService; |
20 | 22 | ||
... | @@ -50,6 +52,15 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide | ... | @@ -50,6 +52,15 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide |
50 | void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries); | 52 | void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries); |
51 | 53 | ||
52 | /** | 54 | /** |
55 | + * Pushes the collection of table statistics entries currently extracted | ||
56 | + * from the given device. | ||
57 | + * | ||
58 | + * @param deviceId device identifier | ||
59 | + * @param tableStatsEntries collection of flow table statistics entries | ||
60 | + */ | ||
61 | + void pushTableStatistics(DeviceId deviceId, List<TableStatisticsEntry> tableStatsEntries); | ||
62 | + | ||
63 | + /** | ||
53 | * Indicates to the core that the requested batch operation has | 64 | * Indicates to the core that the requested batch operation has |
54 | * been completed. | 65 | * been completed. |
55 | * | 66 | * |
... | @@ -57,5 +68,4 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide | ... | @@ -57,5 +68,4 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide |
57 | * @param operation the resulting outcome of the operation | 68 | * @param operation the resulting outcome of the operation |
58 | */ | 69 | */ |
59 | void batchOperationCompleted(long batchId, CompletedBatchOperation operation); | 70 | void batchOperationCompleted(long batchId, CompletedBatchOperation operation); |
60 | - | ||
61 | } | 71 | } | ... | ... |
... | @@ -104,4 +104,11 @@ public interface FlowRuleService | ... | @@ -104,4 +104,11 @@ public interface FlowRuleService |
104 | */ | 104 | */ |
105 | void apply(FlowRuleOperations ops); | 105 | void apply(FlowRuleOperations ops); |
106 | 106 | ||
107 | + /** | ||
108 | + * Returns the collection of flow table statistics of the specified device. | ||
109 | + * | ||
110 | + * @param deviceId device identifier | ||
111 | + * @return collection of flow table statistics | ||
112 | + */ | ||
113 | + Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId); | ||
107 | } | 114 | } | ... | ... |
... | @@ -15,6 +15,8 @@ | ... | @@ -15,6 +15,8 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.net.flow; | 16 | package org.onosproject.net.flow; |
17 | 17 | ||
18 | +import java.util.List; | ||
19 | + | ||
18 | import org.onosproject.net.DeviceId; | 20 | import org.onosproject.net.DeviceId; |
19 | import org.onosproject.store.Store; | 21 | import org.onosproject.store.Store; |
20 | 22 | ||
... | @@ -93,4 +95,23 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe | ... | @@ -93,4 +95,23 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe |
93 | * @return flow_removed event, or null if nothing removed | 95 | * @return flow_removed event, or null if nothing removed |
94 | */ | 96 | */ |
95 | FlowRuleEvent removeFlowRule(FlowEntry rule); | 97 | FlowRuleEvent removeFlowRule(FlowEntry rule); |
98 | + | ||
99 | + /** | ||
100 | + * Updates the flow table statistics of the specified device using | ||
101 | + * the given statistics. | ||
102 | + * | ||
103 | + * @param deviceId device identifier | ||
104 | + * @param tableStats list of table statistics | ||
105 | + * @return ready to send event describing what occurred; | ||
106 | + */ | ||
107 | + FlowRuleEvent updateTableStatistics(DeviceId deviceId, | ||
108 | + List<TableStatisticsEntry> tableStats); | ||
109 | + | ||
110 | + /** | ||
111 | + * Returns the flow table statistics associated with a device. | ||
112 | + * | ||
113 | + * @param deviceId the device ID | ||
114 | + * @return the flow table statistics | ||
115 | + */ | ||
116 | + Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId); | ||
96 | } | 117 | } | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.net.flow; | ||
17 | + | ||
18 | +import org.onosproject.net.DeviceId; | ||
19 | + | ||
20 | +/** | ||
21 | + * Interface for flow table statistics of a device. | ||
22 | + */ | ||
23 | +public interface TableStatisticsEntry { | ||
24 | + | ||
25 | + /** | ||
26 | + * Returns the device Id. | ||
27 | + * | ||
28 | + * @return device id | ||
29 | + */ | ||
30 | + DeviceId deviceId(); | ||
31 | + | ||
32 | + /** | ||
33 | + * Returns the table number. | ||
34 | + * | ||
35 | + * @return table number | ||
36 | + */ | ||
37 | + int tableId(); | ||
38 | + | ||
39 | + /** | ||
40 | + * Returns the number of active flow entries in this table. | ||
41 | + * | ||
42 | + * @return the number of active flow entries | ||
43 | + */ | ||
44 | + long activeFlowEntries(); | ||
45 | + | ||
46 | + /** | ||
47 | + * Returns the number of packets looked up in the table. | ||
48 | + * | ||
49 | + * @return the number of packets looked up in the table | ||
50 | + */ | ||
51 | + long packetsLookedup(); | ||
52 | + | ||
53 | + /** | ||
54 | + * Returns the number of packets that successfully matched in the table. | ||
55 | + * | ||
56 | + * @return the number of packets that successfully matched in the table | ||
57 | + */ | ||
58 | + long packetsMatched(); | ||
59 | +} |
... | @@ -35,17 +35,14 @@ public class FlowRuleServiceAdapter implements FlowRuleService { | ... | @@ -35,17 +35,14 @@ public class FlowRuleServiceAdapter implements FlowRuleService { |
35 | 35 | ||
36 | @Override | 36 | @Override |
37 | public void applyFlowRules(FlowRule... flowRules) { | 37 | public void applyFlowRules(FlowRule... flowRules) { |
38 | - | ||
39 | } | 38 | } |
40 | 39 | ||
41 | @Override | 40 | @Override |
42 | public void removeFlowRules(FlowRule... flowRules) { | 41 | public void removeFlowRules(FlowRule... flowRules) { |
43 | - | ||
44 | } | 42 | } |
45 | 43 | ||
46 | @Override | 44 | @Override |
47 | public void removeFlowRulesById(ApplicationId appId) { | 45 | public void removeFlowRulesById(ApplicationId appId) { |
48 | - | ||
49 | } | 46 | } |
50 | 47 | ||
51 | @Override | 48 | @Override |
... | @@ -60,16 +57,18 @@ public class FlowRuleServiceAdapter implements FlowRuleService { | ... | @@ -60,16 +57,18 @@ public class FlowRuleServiceAdapter implements FlowRuleService { |
60 | 57 | ||
61 | @Override | 58 | @Override |
62 | public void apply(FlowRuleOperations ops) { | 59 | public void apply(FlowRuleOperations ops) { |
63 | - | ||
64 | } | 60 | } |
65 | 61 | ||
66 | @Override | 62 | @Override |
67 | public void addListener(FlowRuleListener listener) { | 63 | public void addListener(FlowRuleListener listener) { |
68 | - | ||
69 | } | 64 | } |
70 | 65 | ||
71 | @Override | 66 | @Override |
72 | public void removeListener(FlowRuleListener listener) { | 67 | public void removeListener(FlowRuleListener listener) { |
68 | + } | ||
73 | 69 | ||
70 | + @Override | ||
71 | + public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) { | ||
72 | + return null; | ||
74 | } | 73 | } |
75 | } | 74 | } | ... | ... |
... | @@ -16,6 +16,7 @@ | ... | @@ -16,6 +16,7 @@ |
16 | package org.onosproject.codec.impl; | 16 | package org.onosproject.codec.impl; |
17 | 17 | ||
18 | import com.google.common.collect.ImmutableSet; | 18 | import com.google.common.collect.ImmutableSet; |
19 | + | ||
19 | import org.apache.felix.scr.annotations.Activate; | 20 | import org.apache.felix.scr.annotations.Activate; |
20 | import org.apache.felix.scr.annotations.Component; | 21 | import org.apache.felix.scr.annotations.Component; |
21 | import org.apache.felix.scr.annotations.Deactivate; | 22 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -36,6 +37,7 @@ import org.onosproject.net.Port; | ... | @@ -36,6 +37,7 @@ import org.onosproject.net.Port; |
36 | import org.onosproject.net.driver.Driver; | 37 | import org.onosproject.net.driver.Driver; |
37 | import org.onosproject.net.flow.FlowEntry; | 38 | import org.onosproject.net.flow.FlowEntry; |
38 | import org.onosproject.net.flow.FlowRule; | 39 | import org.onosproject.net.flow.FlowRule; |
40 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
39 | import org.onosproject.net.flow.TrafficSelector; | 41 | import org.onosproject.net.flow.TrafficSelector; |
40 | import org.onosproject.net.flow.TrafficTreatment; | 42 | import org.onosproject.net.flow.TrafficTreatment; |
41 | import org.onosproject.net.flow.criteria.Criterion; | 43 | import org.onosproject.net.flow.criteria.Criterion; |
... | @@ -99,6 +101,7 @@ public class CodecManager implements CodecService { | ... | @@ -99,6 +101,7 @@ public class CodecManager implements CodecService { |
99 | registerCodec(Driver.class, new DriverCodec()); | 101 | registerCodec(Driver.class, new DriverCodec()); |
100 | registerCodec(GroupBucket.class, new GroupBucketCodec()); | 102 | registerCodec(GroupBucket.class, new GroupBucketCodec()); |
101 | registerCodec(Load.class, new LoadCodec()); | 103 | registerCodec(Load.class, new LoadCodec()); |
104 | + registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec()); | ||
102 | log.info("Started"); | 105 | log.info("Started"); |
103 | } | 106 | } |
104 | 107 | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.codec.impl; | ||
17 | + | ||
18 | +import org.onosproject.codec.CodecContext; | ||
19 | +import org.onosproject.codec.JsonCodec; | ||
20 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
21 | + | ||
22 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
23 | + | ||
24 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
25 | + | ||
26 | +/** | ||
27 | + * Table statistics entry JSON codec. | ||
28 | + */ | ||
29 | +public final class TableStatisticsEntryCodec extends JsonCodec<TableStatisticsEntry> { | ||
30 | + | ||
31 | + @Override | ||
32 | + public ObjectNode encode(TableStatisticsEntry entry, CodecContext context) { | ||
33 | + checkNotNull(entry, "Table Statistics entry cannot be null"); | ||
34 | + | ||
35 | + final ObjectNode result = context.mapper().createObjectNode() | ||
36 | + .put("tableId", entry.tableId()) | ||
37 | + .put("deviceId", entry.deviceId().toString()) | ||
38 | + .put("activeEntries", entry.activeFlowEntries()) | ||
39 | + .put("packetsLookedUp", entry.packetsLookedup()) | ||
40 | + .put("packetsMatched", entry.packetsMatched()); | ||
41 | + | ||
42 | + return result; | ||
43 | + } | ||
44 | + | ||
45 | +} | ||
46 | + |
... | @@ -20,8 +20,10 @@ import com.google.common.cache.CacheBuilder; | ... | @@ -20,8 +20,10 @@ import com.google.common.cache.CacheBuilder; |
20 | import com.google.common.cache.RemovalListener; | 20 | import com.google.common.cache.RemovalListener; |
21 | import com.google.common.cache.RemovalNotification; | 21 | import com.google.common.cache.RemovalNotification; |
22 | import com.google.common.collect.FluentIterable; | 22 | import com.google.common.collect.FluentIterable; |
23 | +import com.google.common.collect.ImmutableList; | ||
23 | import com.google.common.collect.Sets; | 24 | import com.google.common.collect.Sets; |
24 | import com.google.common.util.concurrent.SettableFuture; | 25 | import com.google.common.util.concurrent.SettableFuture; |
26 | + | ||
25 | import org.apache.felix.scr.annotations.Activate; | 27 | import org.apache.felix.scr.annotations.Activate; |
26 | import org.apache.felix.scr.annotations.Component; | 28 | import org.apache.felix.scr.annotations.Component; |
27 | import org.apache.felix.scr.annotations.Deactivate; | 29 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -44,6 +46,7 @@ import org.onosproject.net.flow.FlowRuleEvent.Type; | ... | @@ -44,6 +46,7 @@ import org.onosproject.net.flow.FlowRuleEvent.Type; |
44 | import org.onosproject.net.flow.FlowRuleStore; | 46 | import org.onosproject.net.flow.FlowRuleStore; |
45 | import org.onosproject.net.flow.FlowRuleStoreDelegate; | 47 | import org.onosproject.net.flow.FlowRuleStoreDelegate; |
46 | import org.onosproject.net.flow.StoredFlowEntry; | 48 | import org.onosproject.net.flow.StoredFlowEntry; |
49 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
47 | import org.onosproject.store.AbstractStore; | 50 | import org.onosproject.store.AbstractStore; |
48 | import org.slf4j.Logger; | 51 | import org.slf4j.Logger; |
49 | 52 | ||
... | @@ -79,6 +82,9 @@ public class SimpleFlowRuleStore | ... | @@ -79,6 +82,9 @@ public class SimpleFlowRuleStore |
79 | private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>> | 82 | private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>> |
80 | flowEntries = new ConcurrentHashMap<>(); | 83 | flowEntries = new ConcurrentHashMap<>(); |
81 | 84 | ||
85 | + private final ConcurrentMap<DeviceId, List<TableStatisticsEntry>> | ||
86 | + deviceTableStats = new ConcurrentHashMap<>(); | ||
87 | + | ||
82 | private final AtomicInteger localBatchIdGen = new AtomicInteger(); | 88 | private final AtomicInteger localBatchIdGen = new AtomicInteger(); |
83 | 89 | ||
84 | // TODO: make this configurable | 90 | // TODO: make this configurable |
... | @@ -97,6 +103,7 @@ public class SimpleFlowRuleStore | ... | @@ -97,6 +103,7 @@ public class SimpleFlowRuleStore |
97 | 103 | ||
98 | @Deactivate | 104 | @Deactivate |
99 | public void deactivate() { | 105 | public void deactivate() { |
106 | + deviceTableStats.clear(); | ||
100 | flowEntries.clear(); | 107 | flowEntries.clear(); |
101 | log.info("Stopped"); | 108 | log.info("Stopped"); |
102 | } | 109 | } |
... | @@ -315,4 +322,20 @@ public class SimpleFlowRuleStore | ... | @@ -315,4 +322,20 @@ public class SimpleFlowRuleStore |
315 | } | 322 | } |
316 | } | 323 | } |
317 | } | 324 | } |
325 | + | ||
326 | + @Override | ||
327 | + public FlowRuleEvent updateTableStatistics(DeviceId deviceId, | ||
328 | + List<TableStatisticsEntry> tableStats) { | ||
329 | + deviceTableStats.put(deviceId, tableStats); | ||
330 | + return null; | ||
331 | + } | ||
332 | + | ||
333 | + @Override | ||
334 | + public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) { | ||
335 | + List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId); | ||
336 | + if (tableStats == null) { | ||
337 | + return Collections.emptyList(); | ||
338 | + } | ||
339 | + return ImmutableList.copyOf(tableStats); | ||
340 | + } | ||
318 | } | 341 | } | ... | ... |
... | @@ -22,6 +22,7 @@ import com.google.common.collect.Lists; | ... | @@ -22,6 +22,7 @@ import com.google.common.collect.Lists; |
22 | import com.google.common.collect.Maps; | 22 | import com.google.common.collect.Maps; |
23 | import com.google.common.collect.Multimap; | 23 | import com.google.common.collect.Multimap; |
24 | import com.google.common.collect.Sets; | 24 | import com.google.common.collect.Sets; |
25 | + | ||
25 | import org.apache.felix.scr.annotations.Activate; | 26 | import org.apache.felix.scr.annotations.Activate; |
26 | import org.apache.felix.scr.annotations.Component; | 27 | import org.apache.felix.scr.annotations.Component; |
27 | import org.apache.felix.scr.annotations.Deactivate; | 28 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -58,6 +59,7 @@ import org.onosproject.net.flow.FlowRuleProviderService; | ... | @@ -58,6 +59,7 @@ import org.onosproject.net.flow.FlowRuleProviderService; |
58 | import org.onosproject.net.flow.FlowRuleService; | 59 | import org.onosproject.net.flow.FlowRuleService; |
59 | import org.onosproject.net.flow.FlowRuleStore; | 60 | import org.onosproject.net.flow.FlowRuleStore; |
60 | import org.onosproject.net.flow.FlowRuleStoreDelegate; | 61 | import org.onosproject.net.flow.FlowRuleStoreDelegate; |
62 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
61 | import org.onosproject.net.provider.AbstractProviderService; | 63 | import org.onosproject.net.provider.AbstractProviderService; |
62 | import org.osgi.service.component.ComponentContext; | 64 | import org.osgi.service.component.ComponentContext; |
63 | import org.slf4j.Logger; | 65 | import org.slf4j.Logger; |
... | @@ -448,6 +450,12 @@ public class FlowRuleManager | ... | @@ -448,6 +450,12 @@ public class FlowRuleManager |
448 | operation | 450 | operation |
449 | )); | 451 | )); |
450 | } | 452 | } |
453 | + | ||
454 | + @Override | ||
455 | + public void pushTableStatistics(DeviceId deviceId, | ||
456 | + List<TableStatisticsEntry> tableStats) { | ||
457 | + store.updateTableStatistics(deviceId, tableStats); | ||
458 | + } | ||
451 | } | 459 | } |
452 | 460 | ||
453 | // Store delegate to re-post events emitted from the store. | 461 | // Store delegate to re-post events emitted from the store. |
... | @@ -603,4 +611,10 @@ public class FlowRuleManager | ... | @@ -603,4 +611,10 @@ public class FlowRuleManager |
603 | } | 611 | } |
604 | 612 | ||
605 | } | 613 | } |
614 | + | ||
615 | + @Override | ||
616 | + public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) { | ||
617 | + checkPermission(FLOWRULE_READ); | ||
618 | + return store.getTableStatistics(deviceId); | ||
619 | + } | ||
606 | } | 620 | } | ... | ... |
... | @@ -16,6 +16,7 @@ | ... | @@ -16,6 +16,7 @@ |
16 | package org.onosproject.store.flow.impl; | 16 | package org.onosproject.store.flow.impl; |
17 | 17 | ||
18 | import com.google.common.base.Objects; | 18 | import com.google.common.base.Objects; |
19 | +import com.google.common.collect.ImmutableList; | ||
19 | import com.google.common.collect.ImmutableMap; | 20 | import com.google.common.collect.ImmutableMap; |
20 | import com.google.common.collect.Iterables; | 21 | import com.google.common.collect.Iterables; |
21 | import com.google.common.collect.Maps; | 22 | import com.google.common.collect.Maps; |
... | @@ -57,6 +58,7 @@ import org.onosproject.net.flow.FlowRuleService; | ... | @@ -57,6 +58,7 @@ import org.onosproject.net.flow.FlowRuleService; |
57 | import org.onosproject.net.flow.FlowRuleStore; | 58 | import org.onosproject.net.flow.FlowRuleStore; |
58 | import org.onosproject.net.flow.FlowRuleStoreDelegate; | 59 | import org.onosproject.net.flow.FlowRuleStoreDelegate; |
59 | import org.onosproject.net.flow.StoredFlowEntry; | 60 | import org.onosproject.net.flow.StoredFlowEntry; |
61 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
60 | import org.onosproject.store.AbstractStore; | 62 | import org.onosproject.store.AbstractStore; |
61 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 63 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
62 | import org.onosproject.store.cluster.messaging.ClusterMessage; | 64 | import org.onosproject.store.cluster.messaging.ClusterMessage; |
... | @@ -64,9 +66,16 @@ import org.onosproject.store.cluster.messaging.ClusterMessageHandler; | ... | @@ -64,9 +66,16 @@ import org.onosproject.store.cluster.messaging.ClusterMessageHandler; |
64 | import org.onosproject.store.flow.ReplicaInfoEvent; | 66 | import org.onosproject.store.flow.ReplicaInfoEvent; |
65 | import org.onosproject.store.flow.ReplicaInfoEventListener; | 67 | import org.onosproject.store.flow.ReplicaInfoEventListener; |
66 | import org.onosproject.store.flow.ReplicaInfoService; | 68 | import org.onosproject.store.flow.ReplicaInfoService; |
69 | +import org.onosproject.store.impl.MastershipBasedTimestamp; | ||
70 | +import org.onosproject.store.serializers.KryoNamespaces; | ||
67 | import org.onosproject.store.serializers.KryoSerializer; | 71 | import org.onosproject.store.serializers.KryoSerializer; |
68 | import org.onosproject.store.serializers.StoreSerializer; | 72 | import org.onosproject.store.serializers.StoreSerializer; |
69 | import org.onosproject.store.serializers.custom.DistributedStoreSerializers; | 73 | import org.onosproject.store.serializers.custom.DistributedStoreSerializers; |
74 | +import org.onosproject.store.service.EventuallyConsistentMap; | ||
75 | +import org.onosproject.store.service.EventuallyConsistentMapEvent; | ||
76 | +import org.onosproject.store.service.EventuallyConsistentMapListener; | ||
77 | +import org.onosproject.store.service.StorageService; | ||
78 | +import org.onosproject.store.service.WallClockTimestamp; | ||
70 | import org.osgi.service.component.ComponentContext; | 79 | import org.osgi.service.component.ComponentContext; |
71 | import org.slf4j.Logger; | 80 | import org.slf4j.Logger; |
72 | 81 | ||
... | @@ -151,6 +160,13 @@ public class NewDistributedFlowRuleStore | ... | @@ -151,6 +160,13 @@ public class NewDistributedFlowRuleStore |
151 | private final ScheduledExecutorService backupSenderExecutor = | 160 | private final ScheduledExecutorService backupSenderExecutor = |
152 | Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/flow", "backup-sender")); | 161 | Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/flow", "backup-sender")); |
153 | 162 | ||
163 | + private EventuallyConsistentMap<DeviceId, List<TableStatisticsEntry>> deviceTableStats; | ||
164 | + private final EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> tableStatsListener = | ||
165 | + new InternalTableStatsListener(); | ||
166 | + | ||
167 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
168 | + protected StorageService storageService; | ||
169 | + | ||
154 | protected static final StoreSerializer SERIALIZER = new KryoSerializer() { | 170 | protected static final StoreSerializer SERIALIZER = new KryoSerializer() { |
155 | @Override | 171 | @Override |
156 | protected void setupKryoPool() { | 172 | protected void setupKryoPool() { |
... | @@ -161,6 +177,11 @@ public class NewDistributedFlowRuleStore | ... | @@ -161,6 +177,11 @@ public class NewDistributedFlowRuleStore |
161 | } | 177 | } |
162 | }; | 178 | }; |
163 | 179 | ||
180 | + protected static final KryoNamespace.Builder SERIALIZER_BUILDER = KryoNamespace.newBuilder() | ||
181 | + .register(KryoNamespaces.API) | ||
182 | + .register(MastershipBasedTimestamp.class); | ||
183 | + | ||
184 | + | ||
164 | private IdGenerator idGenerator; | 185 | private IdGenerator idGenerator; |
165 | private NodeId local; | 186 | private NodeId local; |
166 | 187 | ||
... | @@ -186,6 +207,15 @@ public class NewDistributedFlowRuleStore | ... | @@ -186,6 +207,15 @@ public class NewDistributedFlowRuleStore |
186 | TimeUnit.MILLISECONDS); | 207 | TimeUnit.MILLISECONDS); |
187 | } | 208 | } |
188 | 209 | ||
210 | + deviceTableStats = storageService.<DeviceId, List<TableStatisticsEntry>>eventuallyConsistentMapBuilder() | ||
211 | + .withName("onos-flow-table-stats") | ||
212 | + .withSerializer(SERIALIZER_BUILDER) | ||
213 | + .withAntiEntropyPeriod(5, TimeUnit.SECONDS) | ||
214 | + .withTimestampProvider((k, v) -> new WallClockTimestamp()) | ||
215 | + .withTombstonesDisabled() | ||
216 | + .build(); | ||
217 | + deviceTableStats.addListener(tableStatsListener); | ||
218 | + | ||
189 | logConfig("Started"); | 219 | logConfig("Started"); |
190 | } | 220 | } |
191 | 221 | ||
... | @@ -197,6 +227,8 @@ public class NewDistributedFlowRuleStore | ... | @@ -197,6 +227,8 @@ public class NewDistributedFlowRuleStore |
197 | } | 227 | } |
198 | configService.unregisterProperties(getClass(), false); | 228 | configService.unregisterProperties(getClass(), false); |
199 | unregisterMessageHandlers(); | 229 | unregisterMessageHandlers(); |
230 | + deviceTableStats.removeListener(tableStatsListener); | ||
231 | + deviceTableStats.destroy(); | ||
200 | messageHandlingExecutor.shutdownNow(); | 232 | messageHandlingExecutor.shutdownNow(); |
201 | backupSenderExecutor.shutdownNow(); | 233 | backupSenderExecutor.shutdownNow(); |
202 | log.info("Stopped"); | 234 | log.info("Stopped"); |
... | @@ -786,4 +818,36 @@ public class NewDistributedFlowRuleStore | ... | @@ -786,4 +818,36 @@ public class NewDistributedFlowRuleStore |
786 | return backedupDevices; | 818 | return backedupDevices; |
787 | } | 819 | } |
788 | } | 820 | } |
821 | + | ||
822 | + @Override | ||
823 | + public FlowRuleEvent updateTableStatistics(DeviceId deviceId, | ||
824 | + List<TableStatisticsEntry> tableStats) { | ||
825 | + deviceTableStats.put(deviceId, tableStats); | ||
826 | + return null; | ||
827 | + } | ||
828 | + | ||
829 | + @Override | ||
830 | + public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) { | ||
831 | + NodeId master = mastershipService.getMasterFor(deviceId); | ||
832 | + | ||
833 | + if (master == null) { | ||
834 | + log.debug("Failed to getTableStats: No master for {}", deviceId); | ||
835 | + return Collections.emptyList(); | ||
836 | + } | ||
837 | + | ||
838 | + List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId); | ||
839 | + if (tableStats == null) { | ||
840 | + return Collections.emptyList(); | ||
841 | + } | ||
842 | + return ImmutableList.copyOf(tableStats); | ||
843 | + } | ||
844 | + | ||
845 | + private class InternalTableStatsListener | ||
846 | + implements EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> { | ||
847 | + @Override | ||
848 | + public void event(EventuallyConsistentMapEvent<DeviceId, | ||
849 | + List<TableStatisticsEntry>> event) { | ||
850 | + //TODO: Generate an event to listeners (do we need?) | ||
851 | + } | ||
852 | + } | ||
789 | } | 853 | } | ... | ... |
... | @@ -84,6 +84,7 @@ import org.onosproject.net.device.PortStatistics; | ... | @@ -84,6 +84,7 @@ import org.onosproject.net.device.PortStatistics; |
84 | import org.onosproject.net.flow.CompletedBatchOperation; | 84 | import org.onosproject.net.flow.CompletedBatchOperation; |
85 | import org.onosproject.net.flow.DefaultFlowEntry; | 85 | import org.onosproject.net.flow.DefaultFlowEntry; |
86 | import org.onosproject.net.flow.DefaultFlowRule; | 86 | import org.onosproject.net.flow.DefaultFlowRule; |
87 | +import org.onosproject.net.flow.DefaultTableStatisticsEntry; | ||
87 | import org.onosproject.net.flow.DefaultTrafficSelector; | 88 | import org.onosproject.net.flow.DefaultTrafficSelector; |
88 | import org.onosproject.net.flow.DefaultTrafficTreatment; | 89 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
89 | import org.onosproject.net.flow.FlowEntry; | 90 | import org.onosproject.net.flow.FlowEntry; |
... | @@ -95,6 +96,7 @@ import org.onosproject.net.flow.FlowRuleBatchRequest; | ... | @@ -95,6 +96,7 @@ import org.onosproject.net.flow.FlowRuleBatchRequest; |
95 | import org.onosproject.net.flow.FlowRuleEvent; | 96 | import org.onosproject.net.flow.FlowRuleEvent; |
96 | import org.onosproject.net.flow.FlowRuleExtPayLoad; | 97 | import org.onosproject.net.flow.FlowRuleExtPayLoad; |
97 | import org.onosproject.net.flow.StoredFlowEntry; | 98 | import org.onosproject.net.flow.StoredFlowEntry; |
99 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
98 | import org.onosproject.net.flow.criteria.Criterion; | 100 | import org.onosproject.net.flow.criteria.Criterion; |
99 | import org.onosproject.net.flow.criteria.EthCriterion; | 101 | import org.onosproject.net.flow.criteria.EthCriterion; |
100 | import org.onosproject.net.flow.criteria.EthTypeCriterion; | 102 | import org.onosproject.net.flow.criteria.EthTypeCriterion; |
... | @@ -421,7 +423,9 @@ public final class KryoNamespaces { | ... | @@ -421,7 +423,9 @@ public final class KryoNamespaces { |
421 | DefaultAnnotations.class, | 423 | DefaultAnnotations.class, |
422 | PortStatistics.class, | 424 | PortStatistics.class, |
423 | DefaultPortStatistics.class, | 425 | DefaultPortStatistics.class, |
424 | - IntentDomainId.class | 426 | + IntentDomainId.class, |
427 | + TableStatisticsEntry.class, | ||
428 | + DefaultTableStatisticsEntry.class | ||
425 | ) | 429 | ) |
426 | .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class) | 430 | .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class) |
427 | .register(new URISerializer(), URI.class) | 431 | .register(new URISerializer(), URI.class) | ... | ... |
... | @@ -47,6 +47,8 @@ import org.projectfloodlight.openflow.protocol.OFExperimenter; | ... | @@ -47,6 +47,8 @@ import org.projectfloodlight.openflow.protocol.OFExperimenter; |
47 | import org.projectfloodlight.openflow.protocol.OFFactories; | 47 | import org.projectfloodlight.openflow.protocol.OFFactories; |
48 | import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; | 48 | import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; |
49 | import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; | 49 | import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; |
50 | +import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; | ||
51 | +import org.projectfloodlight.openflow.protocol.OFTableStatsReply; | ||
50 | import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; | 52 | import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; |
51 | import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; | 53 | import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; |
52 | import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; | 54 | import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; |
... | @@ -129,6 +131,9 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -129,6 +131,9 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
129 | protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats = | 131 | protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats = |
130 | ArrayListMultimap.create(); | 132 | ArrayListMultimap.create(); |
131 | 133 | ||
134 | + protected Multimap<Dpid, OFTableStatsEntry> fullTableStats = | ||
135 | + ArrayListMultimap.create(); | ||
136 | + | ||
132 | protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats = | 137 | protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats = |
133 | ArrayListMultimap.create(); | 138 | ArrayListMultimap.create(); |
134 | 139 | ||
... | @@ -230,6 +235,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -230,6 +235,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
230 | @Override | 235 | @Override |
231 | public void processPacket(Dpid dpid, OFMessage msg) { | 236 | public void processPacket(Dpid dpid, OFMessage msg) { |
232 | Collection<OFFlowStatsEntry> flowStats; | 237 | Collection<OFFlowStatsEntry> flowStats; |
238 | + Collection<OFTableStatsEntry> tableStats; | ||
233 | Collection<OFGroupStatsEntry> groupStats; | 239 | Collection<OFGroupStatsEntry> groupStats; |
234 | Collection<OFGroupDescStatsEntry> groupDescStats; | 240 | Collection<OFGroupDescStatsEntry> groupDescStats; |
235 | Collection<OFPortStatsEntry> portStats; | 241 | Collection<OFPortStatsEntry> portStats; |
... | @@ -277,6 +283,15 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -277,6 +283,15 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
277 | executorMsgs.submit(new OFMessageHandler(dpid, rep.build())); | 283 | executorMsgs.submit(new OFMessageHandler(dpid, rep.build())); |
278 | } | 284 | } |
279 | break; | 285 | break; |
286 | + case TABLE: | ||
287 | + tableStats = publishTableStats(dpid, (OFTableStatsReply) reply); | ||
288 | + if (tableStats != null) { | ||
289 | + OFTableStatsReply.Builder rep = | ||
290 | + OFFactories.getFactory(msg.getVersion()).buildTableStatsReply(); | ||
291 | + rep.setEntries(Lists.newLinkedList(tableStats)); | ||
292 | + executorMsgs.submit(new OFMessageHandler(dpid, rep.build())); | ||
293 | + } | ||
294 | + break; | ||
280 | case GROUP: | 295 | case GROUP: |
281 | groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply); | 296 | groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply); |
282 | if (groupStats != null) { | 297 | if (groupStats != null) { |
... | @@ -395,6 +410,16 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -395,6 +410,16 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
395 | return null; | 410 | return null; |
396 | } | 411 | } |
397 | 412 | ||
413 | + private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid, | ||
414 | + OFTableStatsReply reply) { | ||
415 | + //TODO: Get rid of synchronized | ||
416 | + fullTableStats.putAll(dpid, reply.getEntries()); | ||
417 | + if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { | ||
418 | + return fullTableStats.removeAll(dpid); | ||
419 | + } | ||
420 | + return null; | ||
421 | + } | ||
422 | + | ||
398 | private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid, | 423 | private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid, |
399 | OFGroupStatsReply reply) { | 424 | OFGroupStatsReply reply) { |
400 | //TODO: Get rid of synchronized | 425 | //TODO: Get rid of synchronized | ... | ... |
... | @@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause; | ... | @@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause; |
21 | import com.google.common.cache.RemovalNotification; | 21 | import com.google.common.cache.RemovalNotification; |
22 | import com.google.common.collect.Maps; | 22 | import com.google.common.collect.Maps; |
23 | import com.google.common.collect.Sets; | 23 | import com.google.common.collect.Sets; |
24 | + | ||
24 | import org.apache.felix.scr.annotations.Activate; | 25 | import org.apache.felix.scr.annotations.Activate; |
25 | import org.apache.felix.scr.annotations.Component; | 26 | import org.apache.felix.scr.annotations.Component; |
26 | import org.apache.felix.scr.annotations.Deactivate; | 27 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; | ... | @@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; |
32 | import org.onosproject.core.ApplicationId; | 33 | import org.onosproject.core.ApplicationId; |
33 | import org.onosproject.net.DeviceId; | 34 | import org.onosproject.net.DeviceId; |
34 | import org.onosproject.net.flow.CompletedBatchOperation; | 35 | import org.onosproject.net.flow.CompletedBatchOperation; |
36 | +import org.onosproject.net.flow.DefaultTableStatisticsEntry; | ||
35 | import org.onosproject.net.flow.FlowEntry; | 37 | import org.onosproject.net.flow.FlowEntry; |
36 | import org.onosproject.net.flow.FlowRule; | 38 | import org.onosproject.net.flow.FlowRule; |
37 | import org.onosproject.net.flow.FlowRuleBatchEntry; | 39 | import org.onosproject.net.flow.FlowRuleBatchEntry; |
... | @@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad; | ... | @@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad; |
40 | import org.onosproject.net.flow.FlowRuleProvider; | 42 | import org.onosproject.net.flow.FlowRuleProvider; |
41 | import org.onosproject.net.flow.FlowRuleProviderRegistry; | 43 | import org.onosproject.net.flow.FlowRuleProviderRegistry; |
42 | import org.onosproject.net.flow.FlowRuleProviderService; | 44 | import org.onosproject.net.flow.FlowRuleProviderService; |
45 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
43 | import org.onosproject.net.provider.AbstractProvider; | 46 | import org.onosproject.net.provider.AbstractProvider; |
44 | import org.onosproject.net.provider.ProviderId; | 47 | import org.onosproject.net.provider.ProviderId; |
45 | import org.onosproject.net.statistic.DefaultLoad; | 48 | import org.onosproject.net.statistic.DefaultLoad; |
... | @@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType; | ... | @@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType; |
58 | import org.projectfloodlight.openflow.protocol.OFFlowMod; | 61 | import org.projectfloodlight.openflow.protocol.OFFlowMod; |
59 | import org.projectfloodlight.openflow.protocol.OFFlowRemoved; | 62 | import org.projectfloodlight.openflow.protocol.OFFlowRemoved; |
60 | import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; | 63 | import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; |
64 | +import org.projectfloodlight.openflow.protocol.OFTableStatsReply; | ||
65 | +import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; | ||
61 | import org.projectfloodlight.openflow.protocol.OFMessage; | 66 | import org.projectfloodlight.openflow.protocol.OFMessage; |
62 | import org.projectfloodlight.openflow.protocol.OFPortStatus; | 67 | import org.projectfloodlight.openflow.protocol.OFPortStatus; |
63 | import org.projectfloodlight.openflow.protocol.OFStatsReply; | 68 | import org.projectfloodlight.openflow.protocol.OFStatsReply; |
... | @@ -70,6 +75,7 @@ import java.util.Collections; | ... | @@ -70,6 +75,7 @@ import java.util.Collections; |
70 | import java.util.Dictionary; | 75 | import java.util.Dictionary; |
71 | import java.util.List; | 76 | import java.util.List; |
72 | import java.util.Map; | 77 | import java.util.Map; |
78 | +import java.util.Objects; | ||
73 | import java.util.Optional; | 79 | import java.util.Optional; |
74 | import java.util.Set; | 80 | import java.util.Set; |
75 | import java.util.Timer; | 81 | import java.util.Timer; |
... | @@ -121,6 +127,8 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -121,6 +127,8 @@ public class OpenFlowRuleProvider extends AbstractProvider |
121 | 127 | ||
122 | // NewAdaptiveFlowStatsCollector Set | 128 | // NewAdaptiveFlowStatsCollector Set |
123 | private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap(); | 129 | private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap(); |
130 | + private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); | ||
131 | + private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap(); | ||
124 | 132 | ||
125 | /** | 133 | /** |
126 | * Creates an OpenFlow host provider. | 134 | * Creates an OpenFlow host provider. |
... | @@ -214,6 +222,9 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -214,6 +222,9 @@ public class OpenFlowRuleProvider extends AbstractProvider |
214 | fsc.start(); | 222 | fsc.start(); |
215 | simpleCollectors.put(new Dpid(sw.getId()), fsc); | 223 | simpleCollectors.put(new Dpid(sw.getId()), fsc); |
216 | } | 224 | } |
225 | + TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency); | ||
226 | + tsc.start(); | ||
227 | + tableStatsCollectors.put(new Dpid(sw.getId()), tsc); | ||
217 | } | 228 | } |
218 | 229 | ||
219 | private void stopCollectors() { | 230 | private void stopCollectors() { |
... | @@ -225,17 +236,19 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -225,17 +236,19 @@ public class OpenFlowRuleProvider extends AbstractProvider |
225 | simpleCollectors.values().forEach(FlowStatsCollector::stop); | 236 | simpleCollectors.values().forEach(FlowStatsCollector::stop); |
226 | simpleCollectors.clear(); | 237 | simpleCollectors.clear(); |
227 | } | 238 | } |
239 | + tableStatsCollectors.values().forEach(TableStatisticsCollector::stop); | ||
240 | + tableStatsCollectors.clear(); | ||
228 | } | 241 | } |
229 | 242 | ||
230 | private void adjustRate() { | 243 | private void adjustRate() { |
231 | DefaultLoad.setPollInterval(flowPollFrequency); | 244 | DefaultLoad.setPollInterval(flowPollFrequency); |
232 | - | ||
233 | if (adaptiveFlowSampling) { | 245 | if (adaptiveFlowSampling) { |
234 | // NewAdaptiveFlowStatsCollector calAndPollInterval | 246 | // NewAdaptiveFlowStatsCollector calAndPollInterval |
235 | afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency)); | 247 | afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency)); |
236 | } else { | 248 | } else { |
237 | simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); | 249 | simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); |
238 | } | 250 | } |
251 | + tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency)); | ||
239 | } | 252 | } |
240 | 253 | ||
241 | @Override | 254 | @Override |
... | @@ -384,6 +397,10 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -384,6 +397,10 @@ public class OpenFlowRuleProvider extends AbstractProvider |
384 | collector.stop(); | 397 | collector.stop(); |
385 | } | 398 | } |
386 | } | 399 | } |
400 | + TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid); | ||
401 | + if (tsc != null) { | ||
402 | + tsc.stop(); | ||
403 | + } | ||
387 | } | 404 | } |
388 | 405 | ||
389 | @Override | 406 | @Override |
... | @@ -413,6 +430,8 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -413,6 +430,8 @@ public class OpenFlowRuleProvider extends AbstractProvider |
413 | case STATS_REPLY: | 430 | case STATS_REPLY: |
414 | if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) { | 431 | if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) { |
415 | pushFlowMetrics(dpid, (OFFlowStatsReply) msg); | 432 | pushFlowMetrics(dpid, (OFFlowStatsReply) msg); |
433 | + } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) { | ||
434 | + pushTableStatistics(dpid, (OFTableStatsReply) msg); | ||
416 | } | 435 | } |
417 | break; | 436 | break; |
418 | case BARRIER_REPLY: | 437 | case BARRIER_REPLY: |
... | @@ -473,7 +492,6 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -473,7 +492,6 @@ public class OpenFlowRuleProvider extends AbstractProvider |
473 | private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) { | 492 | private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) { |
474 | 493 | ||
475 | DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); | 494 | DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); |
476 | - OpenFlowSwitch sw = controller.getSwitch(dpid); | ||
477 | 495 | ||
478 | List<FlowEntry> flowEntries = replies.getEntries().stream() | 496 | List<FlowEntry> flowEntries = replies.getEntries().stream() |
479 | .map(entry -> new FlowEntryBuilder(dpid, entry).build()) | 497 | .map(entry -> new FlowEntryBuilder(dpid, entry).build()) |
... | @@ -512,6 +530,31 @@ public class OpenFlowRuleProvider extends AbstractProvider | ... | @@ -512,6 +530,31 @@ public class OpenFlowRuleProvider extends AbstractProvider |
512 | providerService.pushFlowMetrics(did, flowEntries); | 530 | providerService.pushFlowMetrics(did, flowEntries); |
513 | } | 531 | } |
514 | } | 532 | } |
533 | + | ||
534 | + private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) { | ||
535 | + | ||
536 | + DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); | ||
537 | + List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream() | ||
538 | + .map(entry -> buildTableStatistics(did, entry)) | ||
539 | + .filter(Objects::nonNull) | ||
540 | + .collect(Collectors.toList()); | ||
541 | + providerService.pushTableStatistics(did, tableStatsEntries); | ||
542 | + } | ||
543 | + | ||
544 | + private TableStatisticsEntry buildTableStatistics(DeviceId deviceId, | ||
545 | + OFTableStatsEntry ofEntry) { | ||
546 | + TableStatisticsEntry entry = null; | ||
547 | + if (ofEntry != null) { | ||
548 | + entry = new DefaultTableStatisticsEntry(deviceId, | ||
549 | + ofEntry.getTableId().getValue(), | ||
550 | + ofEntry.getActiveCount(), | ||
551 | + ofEntry.getLookupCount().getValue(), | ||
552 | + ofEntry.getMatchedCount().getValue()); | ||
553 | + } | ||
554 | + | ||
555 | + return entry; | ||
556 | + | ||
557 | + } | ||
515 | } | 558 | } |
516 | 559 | ||
517 | /** | 560 | /** | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 Open Networking Laboratory | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.onosproject.provider.of.flow.impl; | ||
17 | + | ||
18 | +import org.onlab.util.SharedExecutors; | ||
19 | +import org.onosproject.openflow.controller.OpenFlowSwitch; | ||
20 | +import org.onosproject.openflow.controller.RoleState; | ||
21 | +import org.projectfloodlight.openflow.protocol.OFTableStatsRequest; | ||
22 | +import org.slf4j.Logger; | ||
23 | + | ||
24 | +import java.util.Timer; | ||
25 | +import java.util.TimerTask; | ||
26 | + | ||
27 | +import static org.slf4j.LoggerFactory.getLogger; | ||
28 | + | ||
29 | +/** | ||
30 | + * Collects Table statistics for the specified switch. | ||
31 | + */ | ||
32 | +class TableStatisticsCollector { | ||
33 | + | ||
34 | + private final Logger log = getLogger(getClass()); | ||
35 | + | ||
36 | + public static final int SECONDS = 1000; | ||
37 | + | ||
38 | + private final OpenFlowSwitch sw; | ||
39 | + private Timer timer; | ||
40 | + private TimerTask task; | ||
41 | + | ||
42 | + private int pollInterval; | ||
43 | + | ||
44 | + /** | ||
45 | + * Creates a new table statistics collector for the given switch and poll frequency. | ||
46 | + * | ||
47 | + * @param timer timer to use for scheduling | ||
48 | + * @param sw switch to pull | ||
49 | + * @param pollInterval poll frequency in seconds | ||
50 | + */ | ||
51 | + TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) { | ||
52 | + this.timer = timer; | ||
53 | + this.sw = sw; | ||
54 | + this.pollInterval = pollInterval; | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * Adjusts poll frequency. | ||
59 | + * | ||
60 | + * @param pollInterval poll frequency in seconds | ||
61 | + */ | ||
62 | + synchronized void adjustPollInterval(int pollInterval) { | ||
63 | + this.pollInterval = pollInterval; | ||
64 | + task.cancel(); | ||
65 | + task = new InternalTimerTask(); | ||
66 | + timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000); | ||
67 | + } | ||
68 | + | ||
69 | + private class InternalTimerTask extends TimerTask { | ||
70 | + @Override | ||
71 | + public void run() { | ||
72 | + if (sw.getRole() == RoleState.MASTER) { | ||
73 | + log.trace("Collecting stats for {}", sw.getStringId()); | ||
74 | + OFTableStatsRequest request = sw.factory().buildTableStatsRequest() | ||
75 | + .build(); | ||
76 | + sw.sendMsg(request); | ||
77 | + } | ||
78 | + } | ||
79 | + } | ||
80 | + | ||
81 | + public synchronized void start() { | ||
82 | + // Initially start polling quickly. Then drop down to configured value | ||
83 | + log.debug("Starting Table Stats collection thread for {}", sw.getStringId()); | ||
84 | + task = new InternalTimerTask(); | ||
85 | + SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS, | ||
86 | + pollInterval * SECONDS); | ||
87 | + } | ||
88 | + | ||
89 | + public synchronized void stop() { | ||
90 | + log.debug("Stopping Table Stats collection thread for {}", sw.getStringId()); | ||
91 | + task.cancel(); | ||
92 | + task = null; | ||
93 | + } | ||
94 | + | ||
95 | +} |
... | @@ -21,6 +21,7 @@ import java.util.stream.StreamSupport; | ... | @@ -21,6 +21,7 @@ import java.util.stream.StreamSupport; |
21 | 21 | ||
22 | import javax.ws.rs.GET; | 22 | import javax.ws.rs.GET; |
23 | import javax.ws.rs.Path; | 23 | import javax.ws.rs.Path; |
24 | +import javax.ws.rs.PathParam; | ||
24 | import javax.ws.rs.Produces; | 25 | import javax.ws.rs.Produces; |
25 | import javax.ws.rs.QueryParam; | 26 | import javax.ws.rs.QueryParam; |
26 | import javax.ws.rs.core.Context; | 27 | import javax.ws.rs.core.Context; |
... | @@ -31,7 +32,12 @@ import javax.ws.rs.core.UriInfo; | ... | @@ -31,7 +32,12 @@ import javax.ws.rs.core.UriInfo; |
31 | 32 | ||
32 | import org.onosproject.codec.JsonCodec; | 33 | import org.onosproject.codec.JsonCodec; |
33 | import org.onosproject.net.ConnectPoint; | 34 | import org.onosproject.net.ConnectPoint; |
35 | +import org.onosproject.net.Device; | ||
36 | +import org.onosproject.net.DeviceId; | ||
34 | import org.onosproject.net.Link; | 37 | import org.onosproject.net.Link; |
38 | +import org.onosproject.net.device.DeviceService; | ||
39 | +import org.onosproject.net.flow.FlowRuleService; | ||
40 | +import org.onosproject.net.flow.TableStatisticsEntry; | ||
35 | import org.onosproject.net.link.LinkService; | 41 | import org.onosproject.net.link.LinkService; |
36 | import org.onosproject.net.statistic.Load; | 42 | import org.onosproject.net.statistic.Load; |
37 | import org.onosproject.net.statistic.StatisticService; | 43 | import org.onosproject.net.statistic.StatisticService; |
... | @@ -92,4 +98,59 @@ public class StatisticsWebResource extends AbstractWebResource { | ... | @@ -92,4 +98,59 @@ public class StatisticsWebResource extends AbstractWebResource { |
92 | result.set("loads", loads); | 98 | result.set("loads", loads); |
93 | return ok(result).build(); | 99 | return ok(result).build(); |
94 | } | 100 | } |
101 | + | ||
102 | + /** | ||
103 | + * Get table statistics for all tables of all devices. | ||
104 | + * | ||
105 | + * @return JSON encoded array of table statistics | ||
106 | + */ | ||
107 | + @GET | ||
108 | + @Path("flows/tables") | ||
109 | + @Produces(MediaType.APPLICATION_JSON) | ||
110 | + public Response getTableStatistics() { | ||
111 | + final FlowRuleService service = get(FlowRuleService.class); | ||
112 | + final Iterable<Device> devices = get(DeviceService.class).getDevices(); | ||
113 | + final ObjectNode root = mapper().createObjectNode(); | ||
114 | + final ArrayNode rootArrayNode = root.putArray("device-table-statistics"); | ||
115 | + for (final Device device : devices) { | ||
116 | + final ObjectNode deviceStatsNode = mapper().createObjectNode(); | ||
117 | + deviceStatsNode.put("device", device.id().toString()); | ||
118 | + final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics"); | ||
119 | + final Iterable<TableStatisticsEntry> tableStatsEntries = service.getFlowTableStatistics(device.id()); | ||
120 | + if (tableStatsEntries != null) { | ||
121 | + for (final TableStatisticsEntry entry : tableStatsEntries) { | ||
122 | + statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this)); | ||
123 | + } | ||
124 | + } | ||
125 | + rootArrayNode.add(deviceStatsNode); | ||
126 | + } | ||
127 | + | ||
128 | + return ok(root).build(); | ||
129 | + } | ||
130 | + | ||
131 | + /** | ||
132 | + * Get table statistics for all tables of a specified device. | ||
133 | + * | ||
134 | + * @param deviceId device ID | ||
135 | + * @return JSON encoded array of table statistics | ||
136 | + */ | ||
137 | + @GET | ||
138 | + @Path("flows/tables/{deviceId}") | ||
139 | + @Produces(MediaType.APPLICATION_JSON) | ||
140 | + public Response getTableStatisticsByDeviceId(@PathParam("deviceId") String deviceId) { | ||
141 | + final FlowRuleService service = get(FlowRuleService.class); | ||
142 | + final Iterable<TableStatisticsEntry> tableStatisticsEntries = | ||
143 | + service.getFlowTableStatistics(DeviceId.deviceId(deviceId)); | ||
144 | + final ObjectNode root = mapper().createObjectNode(); | ||
145 | + final ArrayNode rootArrayNode = root.putArray("table-statistics"); | ||
146 | + | ||
147 | + final ObjectNode deviceStatsNode = mapper().createObjectNode(); | ||
148 | + deviceStatsNode.put("device", deviceId); | ||
149 | + final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics"); | ||
150 | + for (final TableStatisticsEntry entry : tableStatisticsEntries) { | ||
151 | + statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this)); | ||
152 | + } | ||
153 | + rootArrayNode.add(deviceStatsNode); | ||
154 | + return ok(root).build(); | ||
155 | + } | ||
95 | } | 156 | } | ... | ... |
-
Please register or login to post a comment