diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/TableStatisticsDiscovery.java b/core/api/src/main/java/org/onosproject/net/behaviour/TableStatisticsDiscovery.java new file mode 100644 index 0000000000..84e6c2be52 --- /dev/null +++ b/core/api/src/main/java/org/onosproject/net/behaviour/TableStatisticsDiscovery.java @@ -0,0 +1,34 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.behaviour; + +import org.onosproject.net.driver.HandlerBehaviour; +import org.onosproject.net.flow.TableStatisticsEntry; +import java.util.Collection; + +/** + * Behaviour capable of retrieving table statistics from device. + */ +public interface TableStatisticsDiscovery extends HandlerBehaviour { + + /** + * Returns statistics for all match-action tables currently defined by the device forwarding pipeline. + * + * @return a list of tableStatisticEntry + */ + Collection getTableStatistics(); + +} diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleDriverProvider.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleDriverProvider.java index abda2bfd15..8a3d724bc1 100644 --- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleDriverProvider.java +++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleDriverProvider.java @@ -16,6 +16,7 @@ package org.onosproject.net.flow.impl; +import static com.google.common.collect.Lists.newArrayList; import com.google.common.collect.ImmutableList; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; @@ -25,6 +26,7 @@ import org.onosproject.core.ApplicationId; import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.TableStatisticsDiscovery; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -35,6 +37,7 @@ import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation; import org.onosproject.net.flow.FlowRuleProgrammable; import org.onosproject.net.flow.FlowRuleProvider; import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.provider.AbstractProvider; import org.onosproject.net.provider.ProviderId; import org.slf4j.Logger; @@ -45,6 +48,7 @@ import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.List; import static com.google.common.collect.ImmutableSet.copyOf; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; @@ -196,12 +200,25 @@ class FlowRuleDriverProvider extends AbstractProvider implements FlowRuleProvide } } + private void pollTableStatistics(Device device) { + try { + List tableStatsList = newArrayList(device.as(TableStatisticsDiscovery.class) + .getTableStatistics()); + providerService.pushTableStatistics(device.id(), tableStatsList); + } catch (Exception e) { + log.warn("Exception thrown while polling table statistics for {}", device.id(), e); + } + } + private void pollFlowEntries() { try { deviceService.getAvailableDevices().forEach(device -> { if (mastershipService.isLocalMaster(device.id()) && device.is(FlowRuleProgrammable.class)) { pollDeviceFlowEntries(device); } + if (mastershipService.isLocalMaster(device.id()) && device.is(TableStatisticsDiscovery.class)) { + pollTableStatistics(device); + } }); } catch (Exception e) { log.warn("Exception thrown while polling flows", e); diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeTableStatisticsDiscovery.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeTableStatisticsDiscovery.java new file mode 100644 index 0000000000..f130732dc7 --- /dev/null +++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeTableStatisticsDiscovery.java @@ -0,0 +1,156 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.drivers.p4runtime; + +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.TableStatisticsDiscovery; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.TableId; +import org.onosproject.net.flow.IndexTableId; +import org.onosproject.net.flow.TableStatisticsEntry; +import org.onosproject.net.flow.DefaultTableStatisticsEntry; +import org.onosproject.net.pi.model.PiPipelineModel; +import org.onosproject.net.pi.model.PiPipelineInterpreter; +import org.onosproject.net.pi.model.PiTableId; +import org.onosproject.net.pi.model.PiTableModel; + +import java.util.List; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; +import java.util.ArrayList; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * Implementation of behaviour TableStatisticsDiscovery for P4Runtime. + */ +public class P4RuntimeTableStatisticsDiscovery extends AbstractP4RuntimeHandlerBehaviour + implements TableStatisticsDiscovery { + + @Override + public List getTableStatistics() { + if (!setupBehaviour()) { + return Collections.emptyList(); + } + FlowRuleService flowService = handler().get(FlowRuleService.class); + PiPipelineInterpreter interpreter = getInterpreter(); + PiPipelineModel model = pipeconf.pipelineModel(); + List tableStatsList; + + List rules = newArrayList(flowService.getFlowEntries(deviceId)); + Map piTableFlowCount = piFlowRuleCounting(model, interpreter, rules); + Map piTableMatchCount = piMatchedCounting(model, interpreter, rules); + tableStatsList = generatePiFlowTableStatistics(piTableFlowCount, piTableMatchCount, model, deviceId); + + return tableStatsList; + } + + /** + * Returns the number of added flows in each table. + * + * @param model pipeline model + * @param interpreter pipeline interpreter + * @param rules flow rules in this device + * @return hashmap containing matched packet counting for each table + */ + private Map piFlowRuleCounting(PiPipelineModel model, PiPipelineInterpreter interpreter, + List rules) { + Map piTableFlowCount = new HashMap<>(); + for (PiTableModel tableModel : model.tables()) { + piTableFlowCount.put(tableModel.id(), 0); + } + for (FlowEntry f : rules) { + if (f.state() == FlowEntry.FlowEntryState.ADDED) { + PiTableId piTableId = getPiTableId(f, interpreter); + if (piTableId != null) { + piTableFlowCount.put(piTableId, piTableFlowCount.get(piTableId) + 1); + } + } + } + return piTableFlowCount; + } + + /** + * Returns the number of matched packets for each table. + * + * @param model pipeline model + * @param interpreter pipeline interpreter + * @param rules flow rules in this device + * @return hashmap containing flow rule counting for each table + */ + private Map piMatchedCounting(PiPipelineModel model, PiPipelineInterpreter interpreter, + List rules) { + Map piTableMatchCount = new HashMap<>(); + for (PiTableModel tableModel : model.tables()) { + piTableMatchCount.put(tableModel.id(), (long) 0); + } + for (FlowEntry f : rules) { + if (f.state() == FlowEntry.FlowEntryState.ADDED) { + PiTableId piTableId = getPiTableId(f, interpreter); + if (piTableId != null) { + piTableMatchCount.put(piTableId, piTableMatchCount.get(piTableId) + f.packets()); + } + } + } + return piTableMatchCount; + } + + /** + * Returns the PiTableId of the pipeline independent table that contains the flow rule. If null is returned, it + * means that the given flow rule's table ID is index table ID without a mapping with a pipeline independent table + * ID. + * + * @param flowEntry flow rule + * @param interpreter pipeline interpreter + * @return PiTableId of the table containing input FlowEntry or null + */ + private PiTableId getPiTableId(FlowEntry flowEntry, PiPipelineInterpreter interpreter) { + return flowEntry.table().type() == TableId.Type.PIPELINE_INDEPENDENT ? (PiTableId) flowEntry.table() : + interpreter.mapFlowRuleTableId(((IndexTableId) flowEntry.table()).id()).orElse(null); + } + + /** + * Returns the list of table statistics for P4 switch. + * + * @param piTableFlowCount hashmap containing the number of flow rules for each table + * @param piTableMatchCount hashmap containing the number of matched packets for each table + * @param model pipeline model + * @param deviceId device ID + * @return list of table statistics for P4 switch + */ + private List generatePiFlowTableStatistics(Map piTableFlowCount, + Map piTableMatchCount, + PiPipelineModel model, DeviceId deviceId) { + List tableStatsList; + Iterator it = piTableFlowCount.entrySet().iterator(); + tableStatsList = new ArrayList<>(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + TableStatisticsEntry tableStat = DefaultTableStatisticsEntry.builder() + .withDeviceId(deviceId) + .withTableId((PiTableId) pair.getKey()) + .withActiveFlowEntries(piTableFlowCount.get(pair.getKey())) + .withPacketsMatchedCount(piTableMatchCount.get(pair.getKey())) + .withMaxSize(model.table((PiTableId) pair.getKey()).get().maxSize()).build(); + tableStatsList.add(tableStat); + it.remove(); + } + return tableStatsList; + } +} diff --git a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml index 7464e14978..78188a705c 100644 --- a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml +++ b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml @@ -16,6 +16,8 @@ --> +