Committed by
Gerrit Code Review
[Emu] openTAM: FlowStatisticManager, DistributedFlowStatisticStore, get-flow-sta…
…ts CLI Implementation and NewAdaptiveFlowStatsCollector update and typo - GetFlowStatistics.java .Fixed function name typo: immediateLoad() - SummaryFlowEntryWithLoad.java .Added javadoc - TypedFlowEntryWithLoad.java .Added javadoc, .and replace checknotnull and throw NullPointerException in typedPollInterval() at line 104 Change-Id: I23d2eaf234d0affeb5f927275148d9165c66c774
Showing
11 changed files
with
1731 additions
and
17 deletions
... | @@ -25,6 +25,8 @@ import org.onosproject.net.ElementId; | ... | @@ -25,6 +25,8 @@ import org.onosproject.net.ElementId; |
25 | import org.onosproject.net.Port; | 25 | import org.onosproject.net.Port; |
26 | import org.onosproject.net.flow.FlowRule; | 26 | import org.onosproject.net.flow.FlowRule; |
27 | import org.onosproject.net.group.Group; | 27 | import org.onosproject.net.group.Group; |
28 | + | ||
29 | +import org.onosproject.net.statistic.TypedFlowEntryWithLoad; | ||
28 | import org.onosproject.net.topology.TopologyCluster; | 30 | import org.onosproject.net.topology.TopologyCluster; |
29 | 31 | ||
30 | import java.util.Comparator; | 32 | import java.util.Comparator; |
... | @@ -115,4 +117,12 @@ public final class Comparators { | ... | @@ -115,4 +117,12 @@ public final class Comparators { |
115 | public static final Comparator<Interface> INTERFACES_COMPARATOR = (intf1, intf2) -> | 117 | public static final Comparator<Interface> INTERFACES_COMPARATOR = (intf1, intf2) -> |
116 | CONNECT_POINT_COMPARATOR.compare(intf1.connectPoint(), intf2.connectPoint()); | 118 | CONNECT_POINT_COMPARATOR.compare(intf1.connectPoint(), intf2.connectPoint()); |
117 | 119 | ||
120 | + public static final Comparator<TypedFlowEntryWithLoad> TYPEFLOWENTRY_WITHLOAD_COMPARATOR = | ||
121 | + new Comparator<TypedFlowEntryWithLoad>() { | ||
122 | + @Override | ||
123 | + public int compare(TypedFlowEntryWithLoad fe1, TypedFlowEntryWithLoad fe2) { | ||
124 | + long delta = fe1.load().rate() - fe2.load().rate(); | ||
125 | + return delta == 0 ? 0 : (delta > 0 ? -1 : +1); | ||
126 | + } | ||
127 | + }; | ||
118 | } | 128 | } | ... | ... |
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 | + | ||
17 | +package org.onosproject.cli.net; | ||
18 | + | ||
19 | +import org.apache.karaf.shell.commands.Argument; | ||
20 | +import org.apache.karaf.shell.commands.Command; | ||
21 | +import org.apache.karaf.shell.commands.Option; | ||
22 | +import org.onosproject.cli.AbstractShellCommand; | ||
23 | +import org.onosproject.net.ConnectPoint; | ||
24 | +import org.onosproject.net.Device; | ||
25 | +import org.onosproject.net.DeviceId; | ||
26 | +import org.onosproject.net.Port; | ||
27 | +import org.onosproject.net.PortNumber; | ||
28 | +import org.onosproject.net.device.DeviceService; | ||
29 | +import org.onosproject.net.flow.TypedStoredFlowEntry; | ||
30 | +import org.onosproject.net.flow.instructions.Instruction; | ||
31 | +import org.onosproject.net.statistic.FlowStatisticService; | ||
32 | +import org.onosproject.net.statistic.SummaryFlowEntryWithLoad; | ||
33 | +import org.onosproject.net.statistic.TypedFlowEntryWithLoad; | ||
34 | + | ||
35 | +import java.util.List; | ||
36 | +import java.util.Map; | ||
37 | + | ||
38 | +import static org.onosproject.net.DeviceId.deviceId; | ||
39 | +import static org.onosproject.net.PortNumber.portNumber; | ||
40 | + | ||
41 | +/** | ||
42 | + * Fetches flow statistics with a flow type and instruction type. | ||
43 | + */ | ||
44 | +@Command(scope = "onos", name = "get-flow-stats", | ||
45 | + description = "Fetches flow stats for a connection point with given flow type and instruction type") | ||
46 | +public class GetFlowStatistics extends AbstractShellCommand { | ||
47 | + @Argument(index = 0, name = "devicePort", | ||
48 | + description = "Device[/Port] connectPoint Description", | ||
49 | + required = true, multiValued = false) | ||
50 | + String devicePort = null; | ||
51 | + | ||
52 | + @Option(name = "-s", aliases = "--summary", | ||
53 | + description = "Show flow stats summary", | ||
54 | + required = false, multiValued = false) | ||
55 | + boolean showSummary = true; // default summary | ||
56 | + | ||
57 | + @Option(name = "-a", aliases = "--all", | ||
58 | + description = "Show flow stats all", | ||
59 | + required = false, multiValued = false) | ||
60 | + boolean showAll = false; | ||
61 | + | ||
62 | + @Option(name = "-t", aliases = "--topn", | ||
63 | + description = "Show flow stats topn", | ||
64 | + required = false, multiValued = false) | ||
65 | + String showTopn = null; | ||
66 | + | ||
67 | + @Option(name = "-f", aliases = "--flowType", | ||
68 | + description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN" | ||
69 | + + ", and is valid with -a or -t option only", | ||
70 | + required = false, multiValued = false) | ||
71 | + String flowLiveType = null; | ||
72 | + | ||
73 | + @Option(name = "-i", aliases = "--instructionType", | ||
74 | + description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION," | ||
75 | + + " TABLE, L3MODIFICATION, METADATA" | ||
76 | + + ", and is valid with -a or -t option only", | ||
77 | + required = false, multiValued = false) | ||
78 | + String instructionType = null; | ||
79 | + | ||
80 | + @Override | ||
81 | + protected void execute() { | ||
82 | + DeviceService deviceService = get(DeviceService.class); | ||
83 | + FlowStatisticService flowStatsService = get(FlowStatisticService.class); | ||
84 | + | ||
85 | + String deviceURI = getDeviceId(devicePort); | ||
86 | + String portURI = getPortNumber(devicePort); | ||
87 | + | ||
88 | + DeviceId ingressDeviceId = deviceId(deviceURI); | ||
89 | + PortNumber ingressPortNumber; | ||
90 | + if (portURI.length() == 0) { | ||
91 | + ingressPortNumber = null; | ||
92 | + } else { | ||
93 | + ingressPortNumber = portNumber(portURI); | ||
94 | + } | ||
95 | + | ||
96 | + Device device = deviceService.getDevice(ingressDeviceId); | ||
97 | + if (device == null) { | ||
98 | + error("No such device %s", ingressDeviceId.uri()); | ||
99 | + return; | ||
100 | + } | ||
101 | + | ||
102 | + if (ingressPortNumber != null) { | ||
103 | + Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber); | ||
104 | + if (port == null) { | ||
105 | + error("No such port %s on device %s", portURI, ingressDeviceId.uri()); | ||
106 | + return; | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + if (flowLiveType != null) { | ||
111 | + flowLiveType = flowLiveType.toUpperCase(); | ||
112 | + } | ||
113 | + if (instructionType != null) { | ||
114 | + instructionType = instructionType.toUpperCase(); | ||
115 | + } | ||
116 | + | ||
117 | + // convert String to FlowLiveType and check validity | ||
118 | + TypedStoredFlowEntry.FlowLiveType inLiveType; | ||
119 | + if (flowLiveType == null) { | ||
120 | + inLiveType = null; | ||
121 | + } else { | ||
122 | + inLiveType = getFlowLiveType(flowLiveType); | ||
123 | + if (inLiveType == null) { | ||
124 | + error("Invalid flow live type [%s] error", flowLiveType); | ||
125 | + return; | ||
126 | + } | ||
127 | + } | ||
128 | + // convert String to InstructionType and check validity | ||
129 | + Instruction.Type inInstructionType; | ||
130 | + if (instructionType == null) { | ||
131 | + inInstructionType = null; | ||
132 | + } else { | ||
133 | + inInstructionType = getInstructionType(instructionType); | ||
134 | + if (inInstructionType == null) { | ||
135 | + error("Invalid instruction type [%s] error", instructionType); | ||
136 | + return; | ||
137 | + } | ||
138 | + } | ||
139 | + | ||
140 | + if (showTopn != null) { | ||
141 | + int topn = Integer.parseInt(showTopn); | ||
142 | + | ||
143 | + if (topn <= 0) { | ||
144 | + topn = 100; //default value | ||
145 | + } else if (topn > 1000) { | ||
146 | + topn = 1000; //max value | ||
147 | + } | ||
148 | + | ||
149 | + // print show topn head line with type | ||
150 | + print("deviceId=%s, show TOPN=%s flows, live type=%s, instruction type=%s", | ||
151 | + deviceURI, | ||
152 | + Integer.toString(topn), | ||
153 | + flowLiveType == null ? "ALL" : flowLiveType, | ||
154 | + instructionType == null ? "ALL" : instructionType); | ||
155 | + if (ingressPortNumber == null) { | ||
156 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap = | ||
157 | + flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn); | ||
158 | + // print all ports topn flows load for a given device | ||
159 | + for (ConnectPoint cp : typedFlowLoadMap.keySet()) { | ||
160 | + printPortFlowsLoad(cp, typedFlowLoadMap.get(cp)); | ||
161 | + } | ||
162 | + } else { | ||
163 | + List<TypedFlowEntryWithLoad> typedFlowLoad = | ||
164 | + flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn); | ||
165 | + // print device/port topn flows load | ||
166 | + ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber); | ||
167 | + printPortFlowsLoad(cp, typedFlowLoad); | ||
168 | + } | ||
169 | + } else if (showAll) { // is true? | ||
170 | + // print show all head line with type | ||
171 | + print("deviceId=%s, show ALL flows, live type=%s, instruction type=%s", | ||
172 | + deviceURI, | ||
173 | + flowLiveType == null ? "ALL" : flowLiveType, | ||
174 | + instructionType == null ? "ALL" : instructionType); | ||
175 | + if (ingressPortNumber == null) { | ||
176 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap = | ||
177 | + flowStatsService.loadAllByType(device, inLiveType, inInstructionType); | ||
178 | + // print all ports all flows load for a given device | ||
179 | + for (ConnectPoint cp : typedFlowLoadMap.keySet()) { | ||
180 | + printPortFlowsLoad(cp, typedFlowLoadMap.get(cp)); | ||
181 | + } | ||
182 | + } else { | ||
183 | + List<TypedFlowEntryWithLoad> typedFlowLoad = | ||
184 | + flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType); | ||
185 | + // print device/port all flows load | ||
186 | + ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber); | ||
187 | + printPortFlowsLoad(cp, typedFlowLoad); | ||
188 | + } | ||
189 | + } else { // if (showSummary == true) //always is true | ||
190 | + // print show summary head line | ||
191 | + print("deviceId=%s, show SUMMARY flows", deviceURI); | ||
192 | + if (ingressPortNumber == null) { | ||
193 | + Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap = | ||
194 | + flowStatsService.loadSummary(device); | ||
195 | + // print all ports flow load summary for a given device | ||
196 | + for (ConnectPoint cp : summaryFlowLoadMap.keySet()) { | ||
197 | + printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp)); | ||
198 | + } | ||
199 | + } else { | ||
200 | + SummaryFlowEntryWithLoad summaryFlowLoad = | ||
201 | + flowStatsService.loadSummary(device, ingressPortNumber); | ||
202 | + // print device/port flow load summary | ||
203 | + ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber); | ||
204 | + printPortSummaryLoad(cp, summaryFlowLoad); | ||
205 | + } | ||
206 | + } | ||
207 | + } | ||
208 | + | ||
209 | + /** | ||
210 | + * Extracts the port number portion of the ConnectPoint. | ||
211 | + * | ||
212 | + * @param deviceString string representing the device/port | ||
213 | + * @return port number as a string, empty string if the port is not found | ||
214 | + */ | ||
215 | + private String getPortNumber(String deviceString) { | ||
216 | + if (deviceString == null) { | ||
217 | + return ""; | ||
218 | + } | ||
219 | + | ||
220 | + int slash = deviceString.indexOf('/'); | ||
221 | + if (slash <= 0) { | ||
222 | + return ""; // return when no port number | ||
223 | + } | ||
224 | + return deviceString.substring(slash + 1, deviceString.length()); | ||
225 | + } | ||
226 | + | ||
227 | + /** | ||
228 | + * Extracts the device ID portion of the ConnectPoint. | ||
229 | + * | ||
230 | + * @param deviceString string representing the device/port | ||
231 | + * @return device ID string | ||
232 | + */ | ||
233 | + private String getDeviceId(String deviceString) { | ||
234 | + if (deviceString == null) { | ||
235 | + return ""; | ||
236 | + } | ||
237 | + | ||
238 | + int slash = deviceString.indexOf('/'); | ||
239 | + if (slash <= 0) { | ||
240 | + return deviceString; // return only included device ID | ||
241 | + } | ||
242 | + return deviceString.substring(0, slash); | ||
243 | + } | ||
244 | + | ||
245 | + /** | ||
246 | + * converts string of flow live type to FloeLiveType enum. | ||
247 | + * | ||
248 | + * @param liveType string representing the flow live type | ||
249 | + * @return TypedStoredFlowEntry.FlowLiveType | ||
250 | + */ | ||
251 | + private TypedStoredFlowEntry.FlowLiveType getFlowLiveType(String liveType) { | ||
252 | + String liveTypeUC = liveType.toUpperCase(); | ||
253 | + | ||
254 | + if (liveTypeUC.equals("IMMEDIATE")) { | ||
255 | + return TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW; | ||
256 | + } else if (liveTypeUC.equals("SHORT")) { | ||
257 | + return TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW; | ||
258 | + } else if (liveTypeUC.equals("MID")) { | ||
259 | + return TypedStoredFlowEntry.FlowLiveType.MID_FLOW; | ||
260 | + } else if (liveTypeUC.equals("LONG")) { | ||
261 | + return TypedStoredFlowEntry.FlowLiveType.LONG_FLOW; | ||
262 | + } else if (liveTypeUC.equals("UNKNOWN")) { | ||
263 | + return TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW; | ||
264 | + } else { | ||
265 | + return null; // flow live type error | ||
266 | + } | ||
267 | + } | ||
268 | + | ||
269 | + /** | ||
270 | + * converts string of instruction type to Instruction type enum. | ||
271 | + * | ||
272 | + * @param instType string representing the instruction type | ||
273 | + * @return Instruction.Type | ||
274 | + */ | ||
275 | + private Instruction.Type getInstructionType(String instType) { | ||
276 | + String instTypeUC = instType.toUpperCase(); | ||
277 | + | ||
278 | + if (instTypeUC.equals("DROP")) { | ||
279 | + return Instruction.Type.DROP; | ||
280 | + } else if (instTypeUC.equals("OUTPUT")) { | ||
281 | + return Instruction.Type.OUTPUT; | ||
282 | + } else if (instTypeUC.equals("GROUP")) { | ||
283 | + return Instruction.Type.GROUP; | ||
284 | + } else if (instTypeUC.equals("L0MODIFICATION")) { | ||
285 | + return Instruction.Type.L0MODIFICATION; | ||
286 | + } else if (instTypeUC.equals("L2MODIFICATION")) { | ||
287 | + return Instruction.Type.L2MODIFICATION; | ||
288 | + } else if (instTypeUC.equals("TABLE")) { | ||
289 | + return Instruction.Type.TABLE; | ||
290 | + } else if (instTypeUC.equals("L3MODIFICATION")) { | ||
291 | + return Instruction.Type.L3MODIFICATION; | ||
292 | + } else if (instTypeUC.equals("METADATA")) { | ||
293 | + return Instruction.Type.METADATA; | ||
294 | + } else { | ||
295 | + return null; // instruction type error | ||
296 | + } | ||
297 | + } | ||
298 | + | ||
299 | + private void printPortFlowsLoad(ConnectPoint cp, List<TypedFlowEntryWithLoad> typedFlowLoad) { | ||
300 | + print(" deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size()); | ||
301 | + for (TypedFlowEntryWithLoad tfel: typedFlowLoad) { | ||
302 | + TypedStoredFlowEntry tfe = tfel.typedStoredFlowEntry(); | ||
303 | + print(" flowId=%s, state=%s, liveType=%s, life=%s -> %s", | ||
304 | + Long.toHexString(tfe.id().value()), | ||
305 | + tfe.state(), | ||
306 | + tfe.flowLiveType(), | ||
307 | + tfe.life(), | ||
308 | + tfel.load().isValid() ? tfel.load() : "Load{rate=0, NOT VALID}"); | ||
309 | + } | ||
310 | + } | ||
311 | + | ||
312 | + private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) { | ||
313 | + print(" deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s", | ||
314 | + cp.elementId(), | ||
315 | + cp.port(), | ||
316 | + summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}", | ||
317 | + summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}", | ||
318 | + summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}", | ||
319 | + summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}", | ||
320 | + summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}", | ||
321 | + summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}"); | ||
322 | + } | ||
323 | +} |
... | @@ -222,6 +222,12 @@ | ... | @@ -222,6 +222,12 @@ |
222 | </completers> | 222 | </completers> |
223 | </command> | 223 | </command> |
224 | <command> | 224 | <command> |
225 | + <action class="org.onosproject.cli.net.GetFlowStatistics"/> | ||
226 | + <completers> | ||
227 | + <ref component-id="deviceIdCompleter"/> | ||
228 | + </completers> | ||
229 | + </command> | ||
230 | + <command> | ||
225 | <action class="org.onosproject.cli.net.AddMultiPointToSinglePointIntentCommand"/> | 231 | <action class="org.onosproject.cli.net.AddMultiPointToSinglePointIntentCommand"/> |
226 | <completers> | 232 | <completers> |
227 | <ref component-id="connectPointCompleter"/> | 233 | <ref component-id="connectPointCompleter"/> |
... | @@ -333,7 +339,6 @@ | ... | @@ -333,7 +339,6 @@ |
333 | <command> | 339 | <command> |
334 | <action class="org.onosproject.cli.net.InterfacesListCommand"/> | 340 | <action class="org.onosproject.cli.net.InterfacesListCommand"/> |
335 | </command> | 341 | </command> |
336 | - | ||
337 | <command> | 342 | <command> |
338 | <action class="org.onosproject.cli.net.GroupsListCommand"/> | 343 | <action class="org.onosproject.cli.net.GroupsListCommand"/> |
339 | </command> | 344 | </command> | ... | ... |
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 | + | ||
17 | +package org.onosproject.net.statistic; | ||
18 | + | ||
19 | +import org.onosproject.net.ConnectPoint; | ||
20 | +import org.onosproject.net.Device; | ||
21 | +import org.onosproject.net.PortNumber; | ||
22 | +import org.onosproject.net.flow.TypedStoredFlowEntry; | ||
23 | +import org.onosproject.net.flow.instructions.Instruction; | ||
24 | + | ||
25 | +import java.util.List; | ||
26 | +import java.util.Map; | ||
27 | + | ||
28 | +/** | ||
29 | + * Service for obtaining individual flow statistic information about device and link in the system. | ||
30 | + * Basic statistics are obtained from the StatisticService | ||
31 | + */ | ||
32 | +public interface FlowStatisticService { | ||
33 | + | ||
34 | + /** | ||
35 | + * Obtain the summary load list for the device with the given link. | ||
36 | + * | ||
37 | + * @param device the Device to query. | ||
38 | + * @return map of summary flow entry load | ||
39 | + */ | ||
40 | + Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device); | ||
41 | + | ||
42 | + /** | ||
43 | + * Obtain the summary load for the device with the given link or port. | ||
44 | + * | ||
45 | + * @param device the Device to query. | ||
46 | + * @param pNumber the port number to query. | ||
47 | + * @return summary flow entry load | ||
48 | + */ | ||
49 | + SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber); | ||
50 | + | ||
51 | + /** | ||
52 | + * Obtain the set of the flow type and load list for the device with the given link. | ||
53 | + * | ||
54 | + * @param device the Device to query. | ||
55 | + * @param liveType the FlowLiveType to filter, null means no filtering . | ||
56 | + * @param instType the InstructionType to filter, null means no filtering. | ||
57 | + * @return map of flow entry load | ||
58 | + */ | ||
59 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device, | ||
60 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
61 | + Instruction.Type instType); | ||
62 | + | ||
63 | + /** | ||
64 | + * Obtain the flow type and load list for the device with the given link or port. | ||
65 | + * | ||
66 | + * @param device the Device to query. | ||
67 | + * @param pNumber the port number of the Device to query | ||
68 | + * @param liveType the FlowLiveType to filter, null means no filtering . | ||
69 | + * @param instType the InstructionType to filter, null means no filtering. | ||
70 | + * @return list of flow entry load | ||
71 | + */ | ||
72 | + List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber, | ||
73 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
74 | + Instruction.Type instType); | ||
75 | + | ||
76 | + /** | ||
77 | + * Obtain the set of the flow type and load topn list for the device with the given link. | ||
78 | + * | ||
79 | + * @param device the Device to query. | ||
80 | + * @param liveType the FlowLiveType to filter, null means no filtering . | ||
81 | + * @param instType the InstructionType to filter, null means no filtering. | ||
82 | + * @param topn the top number to filter, null means no filtering. | ||
83 | + * @return map of flow entry load | ||
84 | + */ | ||
85 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device, | ||
86 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
87 | + Instruction.Type instType, | ||
88 | + int topn); | ||
89 | + | ||
90 | + /** | ||
91 | + * Obtain the flow type and load topn list for the device with the given link or port. | ||
92 | + * | ||
93 | + * @param device the Device to query. | ||
94 | + * @param pNumber the port number of the Device to query | ||
95 | + * @param liveType the FlowLiveType to filter, null means no filtering . | ||
96 | + * @param instType the InstructionType to filter, null means no filtering. | ||
97 | + * @return list of flow entry load | ||
98 | + */ | ||
99 | + List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber, | ||
100 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
101 | + Instruction.Type instType, | ||
102 | + int topn); | ||
103 | +} | ||
104 | + | ||
105 | + |
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 | + | ||
17 | +package org.onosproject.net.statistic; | ||
18 | + | ||
19 | +import org.onosproject.net.ConnectPoint; | ||
20 | +import org.onosproject.net.flow.FlowEntry; | ||
21 | +import org.onosproject.net.flow.FlowRule; | ||
22 | + | ||
23 | +import java.util.Set; | ||
24 | + | ||
25 | +/** | ||
26 | + * Flow Store to house the computed statistics. | ||
27 | + */ | ||
28 | +public interface FlowStatisticStore { | ||
29 | + /** | ||
30 | + * Remove entries associated with this rule. | ||
31 | + * | ||
32 | + * @param rule {@link org.onosproject.net.flow.FlowRule} | ||
33 | + */ | ||
34 | + void removeFlowStatistic(FlowRule rule); | ||
35 | + | ||
36 | + /** | ||
37 | + * Adds a flow stats observation for a flow rule. The previous flow will be removed. | ||
38 | + * | ||
39 | + * @param rule a {@link org.onosproject.net.flow.FlowEntry} | ||
40 | + */ | ||
41 | + void addFlowStatistic(FlowEntry rule); | ||
42 | + | ||
43 | + /** | ||
44 | + * Updates a stats observation for a flow rule. The old flow stats will be moved to previous stats. | ||
45 | + * | ||
46 | + * @param rule a {@link org.onosproject.net.flow.FlowEntry} | ||
47 | + */ | ||
48 | + void updateFlowStatistic(FlowEntry rule); | ||
49 | + | ||
50 | + /** | ||
51 | + * Fetches the current observed flow stats values. | ||
52 | + * | ||
53 | + * @param connectPoint the port to fetch information for | ||
54 | + * @return set of current flow rules | ||
55 | + */ | ||
56 | + Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint); | ||
57 | + | ||
58 | + /** | ||
59 | + * Fetches the current observed flow stats values. | ||
60 | + * | ||
61 | + * @param connectPoint the port to fetch information for | ||
62 | + * @return set of current values | ||
63 | + */ | ||
64 | + Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint); | ||
65 | +} |
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 | + | ||
17 | +package org.onosproject.net.statistic; | ||
18 | + | ||
19 | +import org.onosproject.net.ConnectPoint; | ||
20 | + | ||
21 | +/** | ||
22 | + * Summary Load classified by flow live type. | ||
23 | + */ | ||
24 | +public class SummaryFlowEntryWithLoad { | ||
25 | + private ConnectPoint cp; | ||
26 | + private Load totalLoad; | ||
27 | + private Load immediateLoad; | ||
28 | + private Load shortLoad; | ||
29 | + private Load midLoad; | ||
30 | + private Load longLoad; | ||
31 | + private Load unknownLoad; | ||
32 | + | ||
33 | + /** | ||
34 | + * Creates a new summary flow entry having load for the given connect point and total load. | ||
35 | + * | ||
36 | + * @param cp connect point | ||
37 | + * @param totalLoad total load | ||
38 | + */ | ||
39 | + public SummaryFlowEntryWithLoad(ConnectPoint cp, Load totalLoad) { | ||
40 | + this.cp = cp; | ||
41 | + this.totalLoad = totalLoad; | ||
42 | + this.immediateLoad = new DefaultLoad(); | ||
43 | + this.shortLoad = new DefaultLoad(); | ||
44 | + this.midLoad = new DefaultLoad(); | ||
45 | + this.longLoad = new DefaultLoad(); | ||
46 | + this.unknownLoad = new DefaultLoad(); | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * Creates a new summary flow entry having load for the given connect point | ||
51 | + * and total, immediate, short, mid, and long load. | ||
52 | + * | ||
53 | + * @param cp connect point | ||
54 | + * @param totalLoad total load | ||
55 | + * @param immediateLoad immediate load | ||
56 | + * @param shortLoad short load | ||
57 | + * @param midLoad mid load | ||
58 | + * @param longLoad long load | ||
59 | + */ | ||
60 | + public SummaryFlowEntryWithLoad(ConnectPoint cp, | ||
61 | + Load totalLoad, Load immediateLoad, Load shortLoad, Load midLoad, Load longLoad) { | ||
62 | + this.cp = cp; | ||
63 | + this.totalLoad = totalLoad; | ||
64 | + this.immediateLoad = immediateLoad; | ||
65 | + this.shortLoad = shortLoad; | ||
66 | + this.midLoad = midLoad; | ||
67 | + this.longLoad = longLoad; | ||
68 | + this.unknownLoad = new DefaultLoad(); | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * Creates a new summary flow entry having load for the given connect point | ||
73 | + * and total, immediate, short, mid, long, and unknown load. | ||
74 | + * | ||
75 | + * @param cp connect point | ||
76 | + * @param totalLoad total load | ||
77 | + * @param immediateLoad immediate load | ||
78 | + * @param shortLoad short load | ||
79 | + * @param midLoad mid load | ||
80 | + * @param longLoad long load | ||
81 | + * @param unknownLoad long load | ||
82 | + */ | ||
83 | + public SummaryFlowEntryWithLoad(ConnectPoint cp, | ||
84 | + Load totalLoad, Load immediateLoad, | ||
85 | + Load shortLoad, Load midLoad, Load longLoad, Load unknownLoad) { | ||
86 | + this.cp = cp; | ||
87 | + this.totalLoad = totalLoad; | ||
88 | + this.immediateLoad = immediateLoad; | ||
89 | + this.shortLoad = shortLoad; | ||
90 | + this.midLoad = midLoad; | ||
91 | + this.longLoad = longLoad; | ||
92 | + this.unknownLoad = unknownLoad; | ||
93 | + } | ||
94 | + | ||
95 | + /** | ||
96 | + * Returns connect point. | ||
97 | + */ | ||
98 | + public ConnectPoint connectPoint() { | ||
99 | + return cp; | ||
100 | + } | ||
101 | + | ||
102 | + /** | ||
103 | + * Returns total load of connect point. | ||
104 | + */ | ||
105 | + public Load totalLoad() { | ||
106 | + return totalLoad; | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Returns immediate load of connect point. | ||
111 | + */ | ||
112 | + public Load immediateLoad() { | ||
113 | + return immediateLoad; | ||
114 | + } | ||
115 | + | ||
116 | + /** | ||
117 | + * Returns short load of connect point. | ||
118 | + */ | ||
119 | + public Load shortLoad() { | ||
120 | + return shortLoad; | ||
121 | + } | ||
122 | + | ||
123 | + /** | ||
124 | + * Returns mid load of connect point. | ||
125 | + */ | ||
126 | + public Load midLoad() { | ||
127 | + return midLoad; | ||
128 | + } | ||
129 | + | ||
130 | + /** | ||
131 | + * Returns long load of connect point. | ||
132 | + */ | ||
133 | + public Load longLoad() { | ||
134 | + return longLoad; | ||
135 | + } | ||
136 | + | ||
137 | + /** | ||
138 | + * Returns unknown load of connect point. | ||
139 | + */ | ||
140 | + public Load unknownLoad() { | ||
141 | + return unknownLoad; | ||
142 | + } | ||
143 | +} |
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 | + | ||
17 | +package org.onosproject.net.statistic; | ||
18 | + | ||
19 | +import org.onosproject.net.ConnectPoint; | ||
20 | +import org.onosproject.net.flow.FlowEntry; | ||
21 | +import org.onosproject.net.flow.TypedStoredFlowEntry; | ||
22 | +import org.onosproject.net.flow.DefaultTypedFlowEntry; | ||
23 | + | ||
24 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
25 | + | ||
26 | +/** | ||
27 | + * Load of flow entry of flow live type. | ||
28 | + */ | ||
29 | +public class TypedFlowEntryWithLoad { | ||
30 | + private ConnectPoint cp; | ||
31 | + private TypedStoredFlowEntry tfe; | ||
32 | + private Load load; | ||
33 | + | ||
34 | + //TODO: make this variables class, and share with NewAdaptivceFlowStatsCollector class | ||
35 | + private static final int CAL_AND_POLL_INTERVAL = 5; // means SHORT_POLL_INTERVAL | ||
36 | + private static final int MID_POLL_INTERVAL = 10; | ||
37 | + private static final int LONG_POLL_INTERVAL = 15; | ||
38 | + | ||
39 | + | ||
40 | + public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe, Load load) { | ||
41 | + this.cp = cp; | ||
42 | + this.tfe = tfe; | ||
43 | + this.load = load; | ||
44 | + } | ||
45 | + | ||
46 | + public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe) { | ||
47 | + this.cp = cp; | ||
48 | + this.tfe = tfe; | ||
49 | + this.load = new DefaultLoad(tfe.bytes(), 0, typedPollInterval(tfe)); | ||
50 | + } | ||
51 | + | ||
52 | + public TypedFlowEntryWithLoad(ConnectPoint cp, FlowEntry fe) { | ||
53 | + this.cp = cp; | ||
54 | + this.tfe = newTypedStoredFlowEntry(fe); | ||
55 | + this.load = new DefaultLoad(fe.bytes(), 0, typedPollInterval(this.tfe)); | ||
56 | + } | ||
57 | + | ||
58 | + public ConnectPoint connectPoint() { | ||
59 | + return cp; | ||
60 | + } | ||
61 | + public TypedStoredFlowEntry typedStoredFlowEntry() { | ||
62 | + return tfe; | ||
63 | + } | ||
64 | + public Load load() { | ||
65 | + return load; | ||
66 | + } | ||
67 | + public void setLoad(Load load) { | ||
68 | + this.load = load; | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * Returns short polling interval. | ||
73 | + */ | ||
74 | + public static int shortPollInterval() { | ||
75 | + return CAL_AND_POLL_INTERVAL; | ||
76 | + } | ||
77 | + | ||
78 | + /** | ||
79 | + * Returns mid polling interval. | ||
80 | + */ | ||
81 | + public static int midPollInterval() { | ||
82 | + return MID_POLL_INTERVAL; | ||
83 | + } | ||
84 | + | ||
85 | + /** | ||
86 | + * Returns long polling interval. | ||
87 | + */ | ||
88 | + public static int longPollInterval() { | ||
89 | + return LONG_POLL_INTERVAL; | ||
90 | + } | ||
91 | + | ||
92 | + /** | ||
93 | + * Returns average polling interval. | ||
94 | + */ | ||
95 | + public static int avgPollInterval() { | ||
96 | + return (CAL_AND_POLL_INTERVAL + MID_POLL_INTERVAL + LONG_POLL_INTERVAL) / 3; | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * Returns current typed flow entry's polling interval. | ||
101 | + * | ||
102 | + * @param tfe typed flow entry | ||
103 | + */ | ||
104 | + public static long typedPollInterval(TypedStoredFlowEntry tfe) { | ||
105 | + checkNotNull(tfe, "TypedStoredFlowEntry cannot be null"); | ||
106 | + | ||
107 | + switch (tfe.flowLiveType()) { | ||
108 | + case LONG_FLOW: | ||
109 | + return LONG_POLL_INTERVAL; | ||
110 | + case MID_FLOW: | ||
111 | + return MID_POLL_INTERVAL; | ||
112 | + case SHORT_FLOW: | ||
113 | + case IMMEDIATE_FLOW: | ||
114 | + default: | ||
115 | + return CAL_AND_POLL_INTERVAL; | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + /** | ||
120 | + * Creates a new typed flow entry with the given flow entry fe. | ||
121 | + * | ||
122 | + * @param fe flow entry | ||
123 | + */ | ||
124 | + public static TypedStoredFlowEntry newTypedStoredFlowEntry(FlowEntry fe) { | ||
125 | + if (fe == null) { | ||
126 | + return null; | ||
127 | + } | ||
128 | + | ||
129 | + long life = fe.life(); | ||
130 | + | ||
131 | + if (life >= LONG_POLL_INTERVAL) { | ||
132 | + return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.LONG_FLOW); | ||
133 | + } else if (life >= MID_POLL_INTERVAL) { | ||
134 | + return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.MID_FLOW); | ||
135 | + } else if (life >= CAL_AND_POLL_INTERVAL) { | ||
136 | + return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW); | ||
137 | + } else if (life >= 0) { | ||
138 | + return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW); | ||
139 | + } else { // life < 0 | ||
140 | + return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW); | ||
141 | + } | ||
142 | + } | ||
143 | +} |
... | @@ -52,6 +52,20 @@ | ... | @@ -52,6 +52,20 @@ |
52 | 52 | ||
53 | <dependency> | 53 | <dependency> |
54 | <groupId>org.onosproject</groupId> | 54 | <groupId>org.onosproject</groupId> |
55 | + <version>${project.version}</version> | ||
56 | + <artifactId>onos-cli</artifactId> | ||
57 | + </dependency> | ||
58 | + | ||
59 | + <dependency> | ||
60 | + <groupId>org.onosproject</groupId> | ||
61 | + <artifactId>onos-cli</artifactId> | ||
62 | + <version>${project.version}</version> | ||
63 | + <classifier>tests</classifier> | ||
64 | + <scope>test</scope> | ||
65 | + </dependency> | ||
66 | + | ||
67 | + <dependency> | ||
68 | + <groupId>org.onosproject</groupId> | ||
55 | <artifactId>onos-core-common</artifactId> | 69 | <artifactId>onos-core-common</artifactId> |
56 | <version>${project.version}</version> | 70 | <version>${project.version}</version> |
57 | <classifier>tests</classifier> | 71 | <classifier>tests</classifier> | ... | ... |
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 | + | ||
17 | +package org.onosproject.net.statistic.impl; | ||
18 | + | ||
19 | +import com.google.common.base.MoreObjects; | ||
20 | +import com.google.common.base.Predicate; | ||
21 | +import com.google.common.collect.ImmutableSet; | ||
22 | +import org.apache.felix.scr.annotations.Activate; | ||
23 | +import org.apache.felix.scr.annotations.Component; | ||
24 | +import org.apache.felix.scr.annotations.Deactivate; | ||
25 | +import org.apache.felix.scr.annotations.Reference; | ||
26 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
27 | +import org.apache.felix.scr.annotations.Service; | ||
28 | +import org.onosproject.cli.Comparators; | ||
29 | +import org.onosproject.net.ConnectPoint; | ||
30 | +import org.onosproject.net.Device; | ||
31 | +import org.onosproject.net.Port; | ||
32 | +import org.onosproject.net.PortNumber; | ||
33 | +import org.onosproject.net.device.DeviceService; | ||
34 | +import org.onosproject.net.flow.DefaultTypedFlowEntry; | ||
35 | +import org.onosproject.net.flow.FlowEntry; | ||
36 | +import org.onosproject.net.flow.FlowRule; | ||
37 | +import org.onosproject.net.flow.FlowRuleEvent; | ||
38 | +import org.onosproject.net.flow.FlowRuleListener; | ||
39 | +import org.onosproject.net.flow.FlowRuleService; | ||
40 | +import org.onosproject.net.flow.TypedStoredFlowEntry; | ||
41 | +import org.onosproject.net.flow.instructions.Instruction; | ||
42 | +import org.onosproject.net.statistic.DefaultLoad; | ||
43 | +import org.onosproject.net.statistic.FlowStatisticService; | ||
44 | +import org.onosproject.net.statistic.Load; | ||
45 | +import org.onosproject.net.statistic.FlowStatisticStore; | ||
46 | +import org.onosproject.net.statistic.SummaryFlowEntryWithLoad; | ||
47 | +import org.onosproject.net.statistic.TypedFlowEntryWithLoad; | ||
48 | + | ||
49 | +import org.slf4j.Logger; | ||
50 | + | ||
51 | +import java.util.ArrayList; | ||
52 | +import java.util.HashMap; | ||
53 | +import java.util.List; | ||
54 | +import java.util.Map; | ||
55 | +import java.util.Objects; | ||
56 | +import java.util.Set; | ||
57 | +import java.util.TreeMap; | ||
58 | +import java.util.stream.Collectors; | ||
59 | + | ||
60 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
61 | +import static org.onosproject.security.AppGuard.checkPermission; | ||
62 | +import static org.slf4j.LoggerFactory.getLogger; | ||
63 | +import static org.onosproject.security.AppPermission.Type.*; | ||
64 | + | ||
65 | +/** | ||
66 | + * Provides an implementation of the Flow Statistic Service. | ||
67 | + */ | ||
68 | +@Component(immediate = true, enabled = true) | ||
69 | +@Service | ||
70 | +public class FlowStatisticManager implements FlowStatisticService { | ||
71 | + private final Logger log = getLogger(getClass()); | ||
72 | + | ||
73 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
74 | + protected FlowRuleService flowRuleService; | ||
75 | + | ||
76 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
77 | + protected FlowStatisticStore flowStatisticStore; | ||
78 | + | ||
79 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
80 | + protected DeviceService deviceService; | ||
81 | + | ||
82 | + private final InternalFlowRuleStatsListener frListener = new InternalFlowRuleStatsListener(); | ||
83 | + | ||
84 | + @Activate | ||
85 | + public void activate() { | ||
86 | + flowRuleService.addListener(frListener); | ||
87 | + log.info("Started"); | ||
88 | + } | ||
89 | + | ||
90 | + @Deactivate | ||
91 | + public void deactivate() { | ||
92 | + flowRuleService.removeListener(frListener); | ||
93 | + log.info("Stopped"); | ||
94 | + } | ||
95 | + | ||
96 | + @Override | ||
97 | + public Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device) { | ||
98 | + checkPermission(STATISTIC_READ); | ||
99 | + | ||
100 | + Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR); | ||
101 | + | ||
102 | + if (device == null) { | ||
103 | + return summaryLoad; | ||
104 | + } | ||
105 | + | ||
106 | + List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id())); | ||
107 | + | ||
108 | + for (Port port : ports) { | ||
109 | + ConnectPoint cp = new ConnectPoint(device.id(), port.number()); | ||
110 | + SummaryFlowEntryWithLoad sfe = loadSummaryPortInternal(cp); | ||
111 | + summaryLoad.put(cp, sfe); | ||
112 | + } | ||
113 | + | ||
114 | + return summaryLoad; | ||
115 | + } | ||
116 | + | ||
117 | + @Override | ||
118 | + public SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber) { | ||
119 | + checkPermission(STATISTIC_READ); | ||
120 | + | ||
121 | + ConnectPoint cp = new ConnectPoint(device.id(), pNumber); | ||
122 | + return loadSummaryPortInternal(cp); | ||
123 | + } | ||
124 | + | ||
125 | + @Override | ||
126 | + public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device, | ||
127 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
128 | + Instruction.Type instType) { | ||
129 | + checkPermission(STATISTIC_READ); | ||
130 | + | ||
131 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR); | ||
132 | + | ||
133 | + if (device == null) { | ||
134 | + return allLoad; | ||
135 | + } | ||
136 | + | ||
137 | + List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id())); | ||
138 | + | ||
139 | + for (Port port : ports) { | ||
140 | + ConnectPoint cp = new ConnectPoint(device.id(), port.number()); | ||
141 | + List<TypedFlowEntryWithLoad> tfel = loadAllPortInternal(cp, liveType, instType); | ||
142 | + allLoad.put(cp, tfel); | ||
143 | + } | ||
144 | + | ||
145 | + return allLoad; | ||
146 | + } | ||
147 | + | ||
148 | + @Override | ||
149 | + public List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber, | ||
150 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
151 | + Instruction.Type instType) { | ||
152 | + checkPermission(STATISTIC_READ); | ||
153 | + | ||
154 | + ConnectPoint cp = new ConnectPoint(device.id(), pNumber); | ||
155 | + return loadAllPortInternal(cp, liveType, instType); | ||
156 | + } | ||
157 | + | ||
158 | + @Override | ||
159 | + public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device, | ||
160 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
161 | + Instruction.Type instType, | ||
162 | + int topn) { | ||
163 | + checkPermission(STATISTIC_READ); | ||
164 | + | ||
165 | + Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR); | ||
166 | + | ||
167 | + if (device == null) { | ||
168 | + return allLoad; | ||
169 | + } | ||
170 | + | ||
171 | + List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id())); | ||
172 | + | ||
173 | + for (Port port : ports) { | ||
174 | + ConnectPoint cp = new ConnectPoint(device.id(), port.number()); | ||
175 | + List<TypedFlowEntryWithLoad> tfel = loadTopnPortInternal(cp, liveType, instType, topn); | ||
176 | + allLoad.put(cp, tfel); | ||
177 | + } | ||
178 | + | ||
179 | + return allLoad; | ||
180 | + } | ||
181 | + | ||
182 | + @Override | ||
183 | + public List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber, | ||
184 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
185 | + Instruction.Type instType, | ||
186 | + int topn) { | ||
187 | + checkPermission(STATISTIC_READ); | ||
188 | + | ||
189 | + ConnectPoint cp = new ConnectPoint(device.id(), pNumber); | ||
190 | + return loadTopnPortInternal(cp, liveType, instType, topn); | ||
191 | + } | ||
192 | + | ||
193 | + private SummaryFlowEntryWithLoad loadSummaryPortInternal(ConnectPoint cp) { | ||
194 | + checkPermission(STATISTIC_READ); | ||
195 | + | ||
196 | + Set<FlowEntry> currentStats; | ||
197 | + Set<FlowEntry> previousStats; | ||
198 | + | ||
199 | + TypedStatistics typedStatistics; | ||
200 | + synchronized (flowStatisticStore) { | ||
201 | + currentStats = flowStatisticStore.getCurrentFlowStatistic(cp); | ||
202 | + if (currentStats == null) { | ||
203 | + return new SummaryFlowEntryWithLoad(cp, new DefaultLoad()); | ||
204 | + } | ||
205 | + previousStats = flowStatisticStore.getPreviousFlowStatistic(cp); | ||
206 | + if (previousStats == null) { | ||
207 | + return new SummaryFlowEntryWithLoad(cp, new DefaultLoad()); | ||
208 | + } | ||
209 | + // copy to local flow entry | ||
210 | + typedStatistics = new TypedStatistics(currentStats, previousStats); | ||
211 | + | ||
212 | + // Check for validity of this stats data | ||
213 | + checkLoadValidity(currentStats, previousStats); | ||
214 | + } | ||
215 | + | ||
216 | + // current and previous set is not empty! | ||
217 | + Set<FlowEntry> currentSet = typedStatistics.current(); | ||
218 | + Set<FlowEntry> previousSet = typedStatistics.previous(); | ||
219 | + Load totalLoad = new DefaultLoad(aggregateBytesSet(currentSet), aggregateBytesSet(previousSet), | ||
220 | + TypedFlowEntryWithLoad.avgPollInterval()); | ||
221 | + | ||
222 | + Map<FlowRule, TypedStoredFlowEntry> currentMap; | ||
223 | + Map<FlowRule, TypedStoredFlowEntry> previousMap; | ||
224 | + | ||
225 | + currentMap = typedStatistics.currentImmediate(); | ||
226 | + previousMap = typedStatistics.previousImmediate(); | ||
227 | + Load immediateLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap), | ||
228 | + TypedFlowEntryWithLoad.shortPollInterval()); | ||
229 | + | ||
230 | + currentMap = typedStatistics.currentShort(); | ||
231 | + previousMap = typedStatistics.previousShort(); | ||
232 | + Load shortLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap), | ||
233 | + TypedFlowEntryWithLoad.shortPollInterval()); | ||
234 | + | ||
235 | + currentMap = typedStatistics.currentMid(); | ||
236 | + previousMap = typedStatistics.previousMid(); | ||
237 | + Load midLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap), | ||
238 | + TypedFlowEntryWithLoad.midPollInterval()); | ||
239 | + | ||
240 | + currentMap = typedStatistics.currentLong(); | ||
241 | + previousMap = typedStatistics.previousLong(); | ||
242 | + Load longLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap), | ||
243 | + TypedFlowEntryWithLoad.longPollInterval()); | ||
244 | + | ||
245 | + currentMap = typedStatistics.currentUnknown(); | ||
246 | + previousMap = typedStatistics.previousUnknown(); | ||
247 | + Load unknownLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap), | ||
248 | + TypedFlowEntryWithLoad.avgPollInterval()); | ||
249 | + | ||
250 | + return new SummaryFlowEntryWithLoad(cp, totalLoad, immediateLoad, shortLoad, midLoad, longLoad, unknownLoad); | ||
251 | + } | ||
252 | + | ||
253 | + private List<TypedFlowEntryWithLoad> loadAllPortInternal(ConnectPoint cp, | ||
254 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
255 | + Instruction.Type instType) { | ||
256 | + checkPermission(STATISTIC_READ); | ||
257 | + | ||
258 | + List<TypedFlowEntryWithLoad> retTFEL = new ArrayList<>(); | ||
259 | + | ||
260 | + Set<FlowEntry> currentStats; | ||
261 | + Set<FlowEntry> previousStats; | ||
262 | + | ||
263 | + TypedStatistics typedStatistics; | ||
264 | + synchronized (flowStatisticStore) { | ||
265 | + currentStats = flowStatisticStore.getCurrentFlowStatistic(cp); | ||
266 | + if (currentStats == null) { | ||
267 | + return retTFEL; | ||
268 | + } | ||
269 | + previousStats = flowStatisticStore.getPreviousFlowStatistic(cp); | ||
270 | + if (previousStats == null) { | ||
271 | + return retTFEL; | ||
272 | + } | ||
273 | + // copy to local flow entry set | ||
274 | + typedStatistics = new TypedStatistics(currentStats, previousStats); | ||
275 | + | ||
276 | + // Check for validity of this stats data | ||
277 | + checkLoadValidity(currentStats, previousStats); | ||
278 | + } | ||
279 | + | ||
280 | + // current and previous set is not empty! | ||
281 | + boolean isAllLiveType = (liveType == null ? true : false); // null is all live type | ||
282 | + boolean isAllInstType = (instType == null ? true : false); // null is all inst type | ||
283 | + | ||
284 | + Map<FlowRule, TypedStoredFlowEntry> currentMap; | ||
285 | + Map<FlowRule, TypedStoredFlowEntry> previousMap; | ||
286 | + | ||
287 | + if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW) { | ||
288 | + currentMap = typedStatistics.currentImmediate(); | ||
289 | + previousMap = typedStatistics.previousImmediate(); | ||
290 | + | ||
291 | + List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap, | ||
292 | + isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval()); | ||
293 | + if (fel.size() > 0) { | ||
294 | + retTFEL.addAll(fel); | ||
295 | + } | ||
296 | + } | ||
297 | + | ||
298 | + if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW) { | ||
299 | + currentMap = typedStatistics.currentShort(); | ||
300 | + previousMap = typedStatistics.previousShort(); | ||
301 | + | ||
302 | + List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap, | ||
303 | + isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval()); | ||
304 | + if (fel.size() > 0) { | ||
305 | + retTFEL.addAll(fel); | ||
306 | + } | ||
307 | + } | ||
308 | + | ||
309 | + if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.MID_FLOW) { | ||
310 | + currentMap = typedStatistics.currentMid(); | ||
311 | + previousMap = typedStatistics.previousMid(); | ||
312 | + | ||
313 | + List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap, | ||
314 | + isAllInstType, instType, TypedFlowEntryWithLoad.midPollInterval()); | ||
315 | + if (fel.size() > 0) { | ||
316 | + retTFEL.addAll(fel); | ||
317 | + } | ||
318 | + } | ||
319 | + | ||
320 | + if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.LONG_FLOW) { | ||
321 | + currentMap = typedStatistics.currentLong(); | ||
322 | + previousMap = typedStatistics.previousLong(); | ||
323 | + | ||
324 | + List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap, | ||
325 | + isAllInstType, instType, TypedFlowEntryWithLoad.longPollInterval()); | ||
326 | + if (fel.size() > 0) { | ||
327 | + retTFEL.addAll(fel); | ||
328 | + } | ||
329 | + } | ||
330 | + | ||
331 | + if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW) { | ||
332 | + currentMap = typedStatistics.currentUnknown(); | ||
333 | + previousMap = typedStatistics.previousUnknown(); | ||
334 | + | ||
335 | + List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap, | ||
336 | + isAllInstType, instType, TypedFlowEntryWithLoad.avgPollInterval()); | ||
337 | + if (fel.size() > 0) { | ||
338 | + retTFEL.addAll(fel); | ||
339 | + } | ||
340 | + } | ||
341 | + | ||
342 | + return retTFEL; | ||
343 | + } | ||
344 | + | ||
345 | + private List<TypedFlowEntryWithLoad> typedFlowEntryLoadByInstInternal(ConnectPoint cp, | ||
346 | + Map<FlowRule, TypedStoredFlowEntry> currentMap, | ||
347 | + Map<FlowRule, TypedStoredFlowEntry> previousMap, | ||
348 | + boolean isAllInstType, | ||
349 | + Instruction.Type instType, | ||
350 | + int liveTypePollInterval) { | ||
351 | + List<TypedFlowEntryWithLoad> fel = new ArrayList<>(); | ||
352 | + | ||
353 | + for (TypedStoredFlowEntry tfe : currentMap.values()) { | ||
354 | + if (isAllInstType || | ||
355 | + tfe.treatment().allInstructions().stream(). | ||
356 | + filter(i -> i.type() == instType). | ||
357 | + findAny().isPresent()) { | ||
358 | + long currentBytes = tfe.bytes(); | ||
359 | + long previousBytes = previousMap.getOrDefault(tfe, new DefaultTypedFlowEntry((FlowRule) tfe)).bytes(); | ||
360 | + Load fLoad = new DefaultLoad(currentBytes, previousBytes, liveTypePollInterval); | ||
361 | + fel.add(new TypedFlowEntryWithLoad(cp, tfe, fLoad)); | ||
362 | + } | ||
363 | + } | ||
364 | + | ||
365 | + return fel; | ||
366 | + } | ||
367 | + | ||
368 | + private List<TypedFlowEntryWithLoad> loadTopnPortInternal(ConnectPoint cp, | ||
369 | + TypedStoredFlowEntry.FlowLiveType liveType, | ||
370 | + Instruction.Type instType, | ||
371 | + int topn) { | ||
372 | + List<TypedFlowEntryWithLoad> fel = loadAllPortInternal(cp, liveType, instType); | ||
373 | + | ||
374 | + // Sort with descending order of load | ||
375 | + List<TypedFlowEntryWithLoad> tfel = | ||
376 | + fel.stream().sorted(Comparators.TYPEFLOWENTRY_WITHLOAD_COMPARATOR). | ||
377 | + limit(topn).collect(Collectors.toList()); | ||
378 | + | ||
379 | + return tfel; | ||
380 | + } | ||
381 | + | ||
382 | + private long aggregateBytesSet(Set<FlowEntry> setFE) { | ||
383 | + return setFE.stream().mapToLong(FlowEntry::bytes).sum(); | ||
384 | + } | ||
385 | + | ||
386 | + private long aggregateBytesMap(Map<FlowRule, TypedStoredFlowEntry> mapFE) { | ||
387 | + return mapFE.values().stream().mapToLong(FlowEntry::bytes).sum(); | ||
388 | + } | ||
389 | + | ||
390 | + /** | ||
391 | + * Internal data class holding two set of typed flow entries. | ||
392 | + */ | ||
393 | + private static class TypedStatistics { | ||
394 | + private final ImmutableSet<FlowEntry> currentAll; | ||
395 | + private final ImmutableSet<FlowEntry> previousAll; | ||
396 | + | ||
397 | + private final Map<FlowRule, TypedStoredFlowEntry> currentImmediate = new HashMap<>(); | ||
398 | + private final Map<FlowRule, TypedStoredFlowEntry> previousImmediate = new HashMap<>(); | ||
399 | + | ||
400 | + private final Map<FlowRule, TypedStoredFlowEntry> currentShort = new HashMap<>(); | ||
401 | + private final Map<FlowRule, TypedStoredFlowEntry> previousShort = new HashMap<>(); | ||
402 | + | ||
403 | + private final Map<FlowRule, TypedStoredFlowEntry> currentMid = new HashMap<>(); | ||
404 | + private final Map<FlowRule, TypedStoredFlowEntry> previousMid = new HashMap<>(); | ||
405 | + | ||
406 | + private final Map<FlowRule, TypedStoredFlowEntry> currentLong = new HashMap<>(); | ||
407 | + private final Map<FlowRule, TypedStoredFlowEntry> previousLong = new HashMap<>(); | ||
408 | + | ||
409 | + private final Map<FlowRule, TypedStoredFlowEntry> currentUnknown = new HashMap<>(); | ||
410 | + private final Map<FlowRule, TypedStoredFlowEntry> previousUnknown = new HashMap<>(); | ||
411 | + | ||
412 | + public TypedStatistics(Set<FlowEntry> current, Set<FlowEntry> previous) { | ||
413 | + this.currentAll = ImmutableSet.copyOf(checkNotNull(current)); | ||
414 | + this.previousAll = ImmutableSet.copyOf(checkNotNull(previous)); | ||
415 | + | ||
416 | + currentAll.forEach(fe -> { | ||
417 | + TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe); | ||
418 | + | ||
419 | + switch (tfe.flowLiveType()) { | ||
420 | + case IMMEDIATE_FLOW: | ||
421 | + currentImmediate.put(fe, tfe); | ||
422 | + break; | ||
423 | + case SHORT_FLOW: | ||
424 | + currentShort.put(fe, tfe); | ||
425 | + break; | ||
426 | + case MID_FLOW: | ||
427 | + currentMid.put(fe, tfe); | ||
428 | + break; | ||
429 | + case LONG_FLOW: | ||
430 | + currentLong.put(fe, tfe); | ||
431 | + break; | ||
432 | + default: | ||
433 | + currentUnknown.put(fe, tfe); | ||
434 | + break; | ||
435 | + } | ||
436 | + }); | ||
437 | + | ||
438 | + previousAll.forEach(fe -> { | ||
439 | + TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe); | ||
440 | + | ||
441 | + switch (tfe.flowLiveType()) { | ||
442 | + case IMMEDIATE_FLOW: | ||
443 | + if (currentImmediate.containsKey(fe)) { | ||
444 | + previousImmediate.put(fe, tfe); | ||
445 | + } else if (currentShort.containsKey(fe)) { | ||
446 | + previousShort.put(fe, tfe); | ||
447 | + } else if (currentMid.containsKey(fe)) { | ||
448 | + previousMid.put(fe, tfe); | ||
449 | + } else if (currentLong.containsKey(fe)) { | ||
450 | + previousLong.put(fe, tfe); | ||
451 | + } else { | ||
452 | + previousUnknown.put(fe, tfe); | ||
453 | + } | ||
454 | + break; | ||
455 | + case SHORT_FLOW: | ||
456 | + if (currentShort.containsKey(fe)) { | ||
457 | + previousShort.put(fe, tfe); | ||
458 | + } else if (currentMid.containsKey(fe)) { | ||
459 | + previousMid.put(fe, tfe); | ||
460 | + } else if (currentLong.containsKey(fe)) { | ||
461 | + previousLong.put(fe, tfe); | ||
462 | + } else { | ||
463 | + previousUnknown.put(fe, tfe); | ||
464 | + } | ||
465 | + break; | ||
466 | + case MID_FLOW: | ||
467 | + if (currentMid.containsKey(fe)) { | ||
468 | + previousMid.put(fe, tfe); | ||
469 | + } else if (currentLong.containsKey(fe)) { | ||
470 | + previousLong.put(fe, tfe); | ||
471 | + } else { | ||
472 | + previousUnknown.put(fe, tfe); | ||
473 | + } | ||
474 | + break; | ||
475 | + case LONG_FLOW: | ||
476 | + if (currentLong.containsKey(fe)) { | ||
477 | + previousLong.put(fe, tfe); | ||
478 | + } else { | ||
479 | + previousUnknown.put(fe, tfe); | ||
480 | + } | ||
481 | + break; | ||
482 | + default: | ||
483 | + previousUnknown.put(fe, tfe); | ||
484 | + break; | ||
485 | + } | ||
486 | + }); | ||
487 | + } | ||
488 | + | ||
489 | + /** | ||
490 | + * Returns flow entries as the current value. | ||
491 | + * | ||
492 | + * @return flow entries as the current value | ||
493 | + */ | ||
494 | + public ImmutableSet<FlowEntry> current() { | ||
495 | + return currentAll; | ||
496 | + } | ||
497 | + | ||
498 | + /** | ||
499 | + * Returns flow entries as the previous value. | ||
500 | + * | ||
501 | + * @return flow entries as the previous value | ||
502 | + */ | ||
503 | + public ImmutableSet<FlowEntry> previous() { | ||
504 | + return previousAll; | ||
505 | + } | ||
506 | + | ||
507 | + public Map<FlowRule, TypedStoredFlowEntry> currentImmediate() { | ||
508 | + return currentImmediate; | ||
509 | + } | ||
510 | + public Map<FlowRule, TypedStoredFlowEntry> previousImmediate() { | ||
511 | + return previousImmediate; | ||
512 | + } | ||
513 | + public Map<FlowRule, TypedStoredFlowEntry> currentShort() { | ||
514 | + return currentShort; | ||
515 | + } | ||
516 | + public Map<FlowRule, TypedStoredFlowEntry> previousShort() { | ||
517 | + return previousShort; | ||
518 | + } | ||
519 | + public Map<FlowRule, TypedStoredFlowEntry> currentMid() { | ||
520 | + return currentMid; | ||
521 | + } | ||
522 | + public Map<FlowRule, TypedStoredFlowEntry> previousMid() { | ||
523 | + return previousMid; | ||
524 | + } | ||
525 | + public Map<FlowRule, TypedStoredFlowEntry> currentLong() { | ||
526 | + return currentLong; | ||
527 | + } | ||
528 | + public Map<FlowRule, TypedStoredFlowEntry> previousLong() { | ||
529 | + return previousLong; | ||
530 | + } | ||
531 | + public Map<FlowRule, TypedStoredFlowEntry> currentUnknown() { | ||
532 | + return currentUnknown; | ||
533 | + } | ||
534 | + public Map<FlowRule, TypedStoredFlowEntry> previousUnknown() { | ||
535 | + return previousUnknown; | ||
536 | + } | ||
537 | + | ||
538 | + /** | ||
539 | + * Validates values are not empty. | ||
540 | + * | ||
541 | + * @return false if either of the sets is empty. Otherwise, true. | ||
542 | + */ | ||
543 | + public boolean isValid() { | ||
544 | + return !(currentAll.isEmpty() || previousAll.isEmpty()); | ||
545 | + } | ||
546 | + | ||
547 | + @Override | ||
548 | + public int hashCode() { | ||
549 | + return Objects.hash(currentAll, previousAll); | ||
550 | + } | ||
551 | + | ||
552 | + @Override | ||
553 | + public boolean equals(Object obj) { | ||
554 | + if (this == obj) { | ||
555 | + return true; | ||
556 | + } | ||
557 | + if (!(obj instanceof TypedStatistics)) { | ||
558 | + return false; | ||
559 | + } | ||
560 | + final TypedStatistics other = (TypedStatistics) obj; | ||
561 | + return Objects.equals(this.currentAll, other.currentAll) && | ||
562 | + Objects.equals(this.previousAll, other.previousAll); | ||
563 | + } | ||
564 | + | ||
565 | + @Override | ||
566 | + public String toString() { | ||
567 | + return MoreObjects.toStringHelper(this) | ||
568 | + .add("current", currentAll) | ||
569 | + .add("previous", previousAll) | ||
570 | + .toString(); | ||
571 | + } | ||
572 | + } | ||
573 | + | ||
574 | + private void checkLoadValidity(Set<FlowEntry> current, Set<FlowEntry> previous) { | ||
575 | + current.stream().forEach(c -> { | ||
576 | + FlowEntry f = previous.stream().filter(p -> c.equals(p)). | ||
577 | + findAny().orElse(null); | ||
578 | + if (f != null && c.bytes() < f.bytes()) { | ||
579 | + log.debug("FlowStatisticManager:checkLoadValidity():" + | ||
580 | + "Error: " + c + " :Previous bytes=" + f.bytes() + | ||
581 | + " is larger than current bytes=" + c.bytes() + " !!!"); | ||
582 | + } | ||
583 | + }); | ||
584 | + | ||
585 | + } | ||
586 | + | ||
587 | + /** | ||
588 | + * Creates a predicate that checks the instruction type of a flow entry is the same as | ||
589 | + * the specified instruction type. | ||
590 | + * | ||
591 | + * @param instType instruction type to be checked | ||
592 | + * @return predicate | ||
593 | + */ | ||
594 | + private static Predicate<FlowEntry> hasInstructionType(Instruction.Type instType) { | ||
595 | + return new Predicate<FlowEntry>() { | ||
596 | + @Override | ||
597 | + public boolean apply(FlowEntry flowEntry) { | ||
598 | + List<Instruction> allInstructions = flowEntry.treatment().allInstructions(); | ||
599 | + | ||
600 | + return allInstructions.stream().filter(i -> i.type() == instType).findAny().isPresent(); | ||
601 | + } | ||
602 | + }; | ||
603 | + } | ||
604 | + | ||
605 | + /** | ||
606 | + * Internal flow rule event listener for FlowStatisticManager. | ||
607 | + */ | ||
608 | + private class InternalFlowRuleStatsListener implements FlowRuleListener { | ||
609 | + | ||
610 | + @Override | ||
611 | + public void event(FlowRuleEvent event) { | ||
612 | + FlowRule rule = event.subject(); | ||
613 | + switch (event.type()) { | ||
614 | + case RULE_ADDED: | ||
615 | + if (rule instanceof FlowEntry) { | ||
616 | + flowStatisticStore.addFlowStatistic((FlowEntry) rule); | ||
617 | + } | ||
618 | + break; | ||
619 | + case RULE_UPDATED: | ||
620 | + flowStatisticStore.updateFlowStatistic((FlowEntry) rule); | ||
621 | + break; | ||
622 | + case RULE_ADD_REQUESTED: | ||
623 | + break; | ||
624 | + case RULE_REMOVE_REQUESTED: | ||
625 | + break; | ||
626 | + case RULE_REMOVED: | ||
627 | + flowStatisticStore.removeFlowStatistic(rule); | ||
628 | + break; | ||
629 | + default: | ||
630 | + log.warn("Unknown flow rule event {}", event); | ||
631 | + } | ||
632 | + } | ||
633 | + } | ||
634 | +} |
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 | + | ||
17 | +package org.onosproject.store.statistic.impl; | ||
18 | + | ||
19 | +import com.google.common.base.Objects; | ||
20 | +import org.apache.felix.scr.annotations.Activate; | ||
21 | +import org.apache.felix.scr.annotations.Component; | ||
22 | +import org.apache.felix.scr.annotations.Deactivate; | ||
23 | +import org.apache.felix.scr.annotations.Reference; | ||
24 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
25 | +import org.apache.felix.scr.annotations.Service; | ||
26 | +import org.onlab.util.KryoNamespace; | ||
27 | +import org.onlab.util.Tools; | ||
28 | +import org.onosproject.cluster.ClusterService; | ||
29 | +import org.onosproject.cluster.NodeId; | ||
30 | +import org.onosproject.mastership.MastershipService; | ||
31 | +import org.onosproject.net.ConnectPoint; | ||
32 | +import org.onosproject.net.DeviceId; | ||
33 | +import org.onosproject.net.PortNumber; | ||
34 | +import org.onosproject.net.flow.FlowEntry; | ||
35 | +import org.onosproject.net.flow.FlowRule; | ||
36 | +import org.onosproject.net.flow.instructions.Instruction; | ||
37 | +import org.onosproject.net.flow.instructions.Instructions; | ||
38 | +import org.onosproject.net.statistic.FlowStatisticStore; | ||
39 | +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | ||
40 | +import org.onosproject.store.serializers.KryoNamespaces; | ||
41 | +import org.onosproject.store.serializers.KryoSerializer; | ||
42 | +import org.slf4j.Logger; | ||
43 | + | ||
44 | +import java.util.Collections; | ||
45 | +import java.util.HashSet; | ||
46 | +import java.util.Map; | ||
47 | +import java.util.Set; | ||
48 | +import java.util.concurrent.ConcurrentHashMap; | ||
49 | +import java.util.concurrent.ExecutorService; | ||
50 | +import java.util.concurrent.Executors; | ||
51 | +import java.util.concurrent.TimeUnit; | ||
52 | + | ||
53 | +import static org.onlab.util.Tools.groupedThreads; | ||
54 | +import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_CURRENT; | ||
55 | +import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_PREVIOUS; | ||
56 | +import static org.slf4j.LoggerFactory.getLogger; | ||
57 | + | ||
58 | +/** | ||
59 | + * Maintains flow statistics using RPC calls to collect stats from remote instances | ||
60 | + * on demand. | ||
61 | + */ | ||
62 | +@Component(immediate = true) | ||
63 | +@Service | ||
64 | +public class DistributedFlowStatisticStore implements FlowStatisticStore { | ||
65 | + private final Logger log = getLogger(getClass()); | ||
66 | + | ||
67 | + // TODO: Make configurable. | ||
68 | + private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 4; | ||
69 | + | ||
70 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
71 | + protected MastershipService mastershipService; | ||
72 | + | ||
73 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
74 | + protected ClusterCommunicationService clusterCommunicator; | ||
75 | + | ||
76 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
77 | + protected ClusterService clusterService; | ||
78 | + | ||
79 | + private Map<ConnectPoint, Set<FlowEntry>> previous = | ||
80 | + new ConcurrentHashMap<>(); | ||
81 | + | ||
82 | + private Map<ConnectPoint, Set<FlowEntry>> current = | ||
83 | + new ConcurrentHashMap<>(); | ||
84 | + | ||
85 | + protected static final KryoSerializer SERIALIZER = new KryoSerializer() { | ||
86 | + @Override | ||
87 | + protected void setupKryoPool() { | ||
88 | + serializerPool = KryoNamespace.newBuilder() | ||
89 | + .register(KryoNamespaces.API) | ||
90 | + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) | ||
91 | + // register this store specific classes here | ||
92 | + .build(); | ||
93 | + } | ||
94 | + }; | ||
95 | + | ||
96 | + private NodeId local; | ||
97 | + private ExecutorService messageHandlingExecutor; | ||
98 | + | ||
99 | + private static final long STATISTIC_STORE_TIMEOUT_MILLIS = 3000; | ||
100 | + | ||
101 | + @Activate | ||
102 | + public void activate() { | ||
103 | + local = clusterService.getLocalNode().id(); | ||
104 | + | ||
105 | + messageHandlingExecutor = Executors.newFixedThreadPool( | ||
106 | + MESSAGE_HANDLER_THREAD_POOL_SIZE, | ||
107 | + groupedThreads("onos/store/statistic", "message-handlers")); | ||
108 | + | ||
109 | + clusterCommunicator.addSubscriber( | ||
110 | + GET_CURRENT, SERIALIZER::decode, this::getCurrentStatisticInternal, SERIALIZER::encode, | ||
111 | + messageHandlingExecutor); | ||
112 | + | ||
113 | + clusterCommunicator.addSubscriber( | ||
114 | + GET_CURRENT, SERIALIZER::decode, this::getPreviousStatisticInternal, SERIALIZER::encode, | ||
115 | + messageHandlingExecutor); | ||
116 | + | ||
117 | + log.info("Started"); | ||
118 | + } | ||
119 | + | ||
120 | + @Deactivate | ||
121 | + public void deactivate() { | ||
122 | + clusterCommunicator.removeSubscriber(GET_PREVIOUS); | ||
123 | + clusterCommunicator.removeSubscriber(GET_CURRENT); | ||
124 | + messageHandlingExecutor.shutdown(); | ||
125 | + log.info("Stopped"); | ||
126 | + } | ||
127 | + | ||
128 | + @Override | ||
129 | + public synchronized void removeFlowStatistic(FlowRule rule) { | ||
130 | + ConnectPoint cp = buildConnectPoint(rule); | ||
131 | + if (cp == null) { | ||
132 | + return; | ||
133 | + } | ||
134 | + | ||
135 | + // remove this rule if present from current map | ||
136 | + current.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; }); | ||
137 | + | ||
138 | + // remove this on if present from previous map | ||
139 | + previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; }); | ||
140 | + } | ||
141 | + | ||
142 | + @Override | ||
143 | + public synchronized void addFlowStatistic(FlowEntry rule) { | ||
144 | + ConnectPoint cp = buildConnectPoint(rule); | ||
145 | + if (cp == null) { | ||
146 | + return; | ||
147 | + } | ||
148 | + | ||
149 | + // create one if absent and add this rule | ||
150 | + current.putIfAbsent(cp, new HashSet<>()); | ||
151 | + current.computeIfPresent(cp, (c, e) -> { e.add(rule); return e; }); | ||
152 | + | ||
153 | + // remove previous one if present | ||
154 | + previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; }); | ||
155 | + } | ||
156 | + | ||
157 | + public synchronized void updateFlowStatistic(FlowEntry rule) { | ||
158 | + ConnectPoint cp = buildConnectPoint(rule); | ||
159 | + if (cp == null) { | ||
160 | + return; | ||
161 | + } | ||
162 | + | ||
163 | + Set<FlowEntry> curr = current.get(cp); | ||
164 | + if (curr == null) { | ||
165 | + addFlowStatistic(rule); | ||
166 | + } else { | ||
167 | + FlowEntry f = curr.stream().filter(c -> rule.equals(c)). | ||
168 | + findAny().orElse(null); | ||
169 | + if (rule.bytes() < f.bytes()) { | ||
170 | + log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" + | ||
171 | + " Invalid Flow Update! Will be removed!!" + | ||
172 | + " curr flowId=" + Long.toHexString(rule.id().value()) + | ||
173 | + ", prev flowId=" + Long.toHexString(f.id().value()) + | ||
174 | + ", curr bytes=" + rule.bytes() + ", prev bytes=" + f.bytes() + | ||
175 | + ", curr life=" + rule.life() + ", prev life=" + f.life() + | ||
176 | + ", curr lastSeen=" + rule.lastSeen() + ", prev lastSeen=" + f.lastSeen()); | ||
177 | + // something is wrong! invalid flow entry, so delete it | ||
178 | + removeFlowStatistic(rule); | ||
179 | + return; | ||
180 | + } | ||
181 | + Set<FlowEntry> prev = previous.get(cp); | ||
182 | + if (prev == null) { | ||
183 | + prev = new HashSet<>(); | ||
184 | + previous.put(cp, prev); | ||
185 | + } | ||
186 | + | ||
187 | + // previous one is exist | ||
188 | + if (f != null) { | ||
189 | + // remove old one and add new one | ||
190 | + prev.remove(rule); | ||
191 | + if (!prev.add(f)) { | ||
192 | + log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" + | ||
193 | + " flowId={}, add failed into previous.", | ||
194 | + Long.toHexString(rule.id().value())); | ||
195 | + } | ||
196 | + } | ||
197 | + | ||
198 | + // remove old one and add new one | ||
199 | + curr.remove(rule); | ||
200 | + if (!curr.add(rule)) { | ||
201 | + log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" + | ||
202 | + " flowId={}, add failed into current.", | ||
203 | + Long.toHexString(rule.id().value())); | ||
204 | + } | ||
205 | + } | ||
206 | + } | ||
207 | + | ||
208 | + @Override | ||
209 | + public Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint) { | ||
210 | + final DeviceId deviceId = connectPoint.deviceId(); | ||
211 | + | ||
212 | + NodeId master = mastershipService.getMasterFor(deviceId); | ||
213 | + if (master == null) { | ||
214 | + log.warn("No master for {}", deviceId); | ||
215 | + return Collections.emptySet(); | ||
216 | + } | ||
217 | + | ||
218 | + if (Objects.equal(local, master)) { | ||
219 | + return getCurrentStatisticInternal(connectPoint); | ||
220 | + } else { | ||
221 | + return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive( | ||
222 | + connectPoint, | ||
223 | + GET_CURRENT, | ||
224 | + SERIALIZER::encode, | ||
225 | + SERIALIZER::decode, | ||
226 | + master), | ||
227 | + STATISTIC_STORE_TIMEOUT_MILLIS, | ||
228 | + TimeUnit.MILLISECONDS, | ||
229 | + Collections.emptySet()); | ||
230 | + } | ||
231 | + } | ||
232 | + | ||
233 | + private synchronized Set<FlowEntry> getCurrentStatisticInternal(ConnectPoint connectPoint) { | ||
234 | + return current.get(connectPoint); | ||
235 | + } | ||
236 | + | ||
237 | + @Override | ||
238 | + public Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint) { | ||
239 | + final DeviceId deviceId = connectPoint.deviceId(); | ||
240 | + | ||
241 | + NodeId master = mastershipService.getMasterFor(deviceId); | ||
242 | + if (master == null) { | ||
243 | + log.warn("No master for {}", deviceId); | ||
244 | + return Collections.emptySet(); | ||
245 | + } | ||
246 | + | ||
247 | + if (Objects.equal(local, master)) { | ||
248 | + return getPreviousStatisticInternal(connectPoint); | ||
249 | + } else { | ||
250 | + return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive( | ||
251 | + connectPoint, | ||
252 | + GET_PREVIOUS, | ||
253 | + SERIALIZER::encode, | ||
254 | + SERIALIZER::decode, | ||
255 | + master), | ||
256 | + STATISTIC_STORE_TIMEOUT_MILLIS, | ||
257 | + TimeUnit.MILLISECONDS, | ||
258 | + Collections.emptySet()); | ||
259 | + } | ||
260 | + } | ||
261 | + | ||
262 | + private synchronized Set<FlowEntry> getPreviousStatisticInternal(ConnectPoint connectPoint) { | ||
263 | + return previous.get(connectPoint); | ||
264 | + } | ||
265 | + | ||
266 | + private ConnectPoint buildConnectPoint(FlowRule rule) { | ||
267 | + PortNumber port = getOutput(rule); | ||
268 | + | ||
269 | + if (port == null) { | ||
270 | + return null; | ||
271 | + } | ||
272 | + ConnectPoint cp = new ConnectPoint(rule.deviceId(), port); | ||
273 | + return cp; | ||
274 | + } | ||
275 | + | ||
276 | + private PortNumber getOutput(FlowRule rule) { | ||
277 | + for (Instruction i : rule.treatment().allInstructions()) { | ||
278 | + if (i.type() == Instruction.Type.OUTPUT) { | ||
279 | + Instructions.OutputInstruction out = (Instructions.OutputInstruction) i; | ||
280 | + return out.port(); | ||
281 | + } | ||
282 | + if (i.type() == Instruction.Type.DROP) { | ||
283 | + return PortNumber.P0; | ||
284 | + } | ||
285 | + } | ||
286 | + return null; | ||
287 | + } | ||
288 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -717,25 +717,9 @@ public class NewAdaptiveFlowStatsCollector { | ... | @@ -717,25 +717,9 @@ public class NewAdaptiveFlowStatsCollector { |
717 | long curTime = (cTime > 0 ? cTime : System.currentTimeMillis()); | 717 | long curTime = (cTime > 0 ? cTime : System.currentTimeMillis()); |
718 | // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply | 718 | // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply |
719 | long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000); | 719 | long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000); |
720 | - | ||
721 | // fe.life() unit is SECOND! | 720 | // fe.life() unit is SECOND! |
722 | long liveTime = fe.life() + fromLastSeen; | 721 | long liveTime = fe.life() + fromLastSeen; |
723 | 722 | ||
724 | - // check flow timeout | ||
725 | - if (fe.timeout() > calAndPollInterval && fromLastSeen > fe.timeout()) { | ||
726 | - if (!fe.isPermanent()) { | ||
727 | - log.debug("checkAndMoveLiveFlowInternal, FlowId=" + Long.toHexString(fe.id().value()) | ||
728 | - + ", liveType=" + fe.flowLiveType() | ||
729 | - + ", liveTime=" + liveTime | ||
730 | - + ", life=" + fe.life() | ||
731 | - + ", fromLastSeen=" + fromLastSeen | ||
732 | - + ", timeout=" + fe.timeout() | ||
733 | - + ", isPermanent=" + fe.isPermanent() | ||
734 | - + " AdaptiveStats collection thread for {}", | ||
735 | - sw.getStringId()); | ||
736 | - return false; | ||
737 | - } | ||
738 | - } | ||
739 | 723 | ||
740 | switch (fe.flowLiveType()) { | 724 | switch (fe.flowLiveType()) { |
741 | case IMMEDIATE_FLOW: | 725 | case IMMEDIATE_FLOW: | ... | ... |
-
Please register or login to post a comment